* [PATCH v6 6/7] ASoC: dt-bindings: fsl_easrc: Add document for EASRC
From: Shengjiu Wang @ 2020-04-01 8:45 UTC (permalink / raw)
To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1585726761.git.shengjiu.wang@nxp.com>
EASRC (Enhanced Asynchronous Sample Rate Converter) is a new
IP module found on i.MX8MN.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
.../devicetree/bindings/sound/fsl,easrc.yaml | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,easrc.yaml
diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
new file mode 100644
index 000000000000..14ea60084420
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl,easrc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP Asynchronous Sample Rate Converter (ASRC) Controller
+
+maintainers:
+ - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+properties:
+ $nodename:
+ pattern: "^easrc@.*"
+
+ compatible:
+ const: fsl,imx8mn-easrc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Peripheral clock
+
+ clock-names:
+ items:
+ - const: mem
+
+ dmas:
+ maxItems: 8
+
+ dma-names:
+ items:
+ - const: ctx0_rx
+ - const: ctx0_tx
+ - const: ctx1_rx
+ - const: ctx1_tx
+ - const: ctx2_rx
+ - const: ctx2_tx
+ - const: ctx3_rx
+ - const: ctx3_tx
+
+ firmware-name:
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/string
+ - const: imx/easrc/easrc-imx8mn.bin
+ description: The coefficient table for the filters
+
+ fsl,asrc-rate:
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - minimum: 8000
+ - maximum: 192000
+ description: Defines a mutual sample rate used by DPCM Back Ends
+
+ fsl,asrc-format:
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [2, 6, 10, 32, 36]
+ default: 2
+ description:
+ Defines a mutual sample format used by DPCM Back Ends
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - dmas
+ - dma-names
+ - firmware-name
+ - fsl,asrc-rate
+ - fsl,asrc-format
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8mn-clock.h>
+
+ easrc: easrc@300C0000 {
+ compatible = "fsl,imx8mn-easrc";
+ reg = <0x0 0x300C0000 0x0 0x10000>;
+ interrupts = <0x0 122 0x4>;
+ clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
+ clock-names = "mem";
+ dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
+ <&sdma2 18 23 0> , <&sdma2 19 23 0>,
+ <&sdma2 20 23 0> , <&sdma2 21 23 0>,
+ <&sdma2 22 23 0> , <&sdma2 23 23 0>;
+ dma-names = "ctx0_rx", "ctx0_tx",
+ "ctx1_rx", "ctx1_tx",
+ "ctx2_rx", "ctx2_tx",
+ "ctx3_rx", "ctx3_tx";
+ firmware-name = "imx/easrc/easrc-imx8mn.bin";
+ fsl,asrc-rate = <8000>;
+ fsl,asrc-format = <2>;
+ };
--
2.21.0
^ permalink raw reply related
* [PATCH v6 7/7] ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers
From: Shengjiu Wang @ 2020-04-01 8:45 UTC (permalink / raw)
To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1585726761.git.shengjiu.wang@nxp.com>
EASRC (Enhanced Asynchronous Sample Rate Converter) is a new IP module
found on i.MX8MN. It is different with old ASRC module.
The primary features for the EASRC are as follows:
- 4 Contexts - groups of channels with an independent time base
- Fully independent and concurrent context control
- Simultaneous processing of up to 32 audio channels
- Programmable filter charachteristics for each context
- 32, 24, 20, and 16-bit fixed point audio sample support
- 32-bit floating point audio sample support
- 8kHz to 384kHz sample rate
- 1/16 to 8x sample rate conversion ratio
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Signed-off-by: Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>
---
sound/soc/fsl/Kconfig | 11 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_easrc.c | 2114 +++++++++++++++++++++++++++++++++++++
sound/soc/fsl/fsl_easrc.h | 651 ++++++++++++
4 files changed, 2778 insertions(+)
create mode 100644 sound/soc/fsl/fsl_easrc.c
create mode 100644 sound/soc/fsl/fsl_easrc.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 65e8cd4be930..ea7b4787a8af 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -84,6 +84,17 @@ config SND_SOC_FSL_MICFIL
Say Y if you want to add Pulse Density Modulation microphone
interface (MICFIL) support for NXP.
+config SND_SOC_FSL_EASRC
+ tristate "Enhanced Asynchronous Sample Rate Converter (EASRC) module support"
+ depends on SND_SOC_FSL_ASRC
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y if you want to add Enhanced ASRC support for NXP. The ASRC is
+ a digital module that converts audio from a source sample rate to a
+ destination sample rate. It is a new design module compare with the
+ old ASRC.
+
config SND_SOC_FSL_UTILS
tristate
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8cde88c72d93..b835eebf8825 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -24,6 +24,7 @@ snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
snd-soc-fsl-mqs-objs := fsl_mqs.o
+snd-soc-fsl-easrc-objs := fsl_easrc.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -35,6 +36,7 @@ obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
+obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
# MPC5200 Platform Support
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
new file mode 100644
index 000000000000..809041981700
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -0,0 +1,2114 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/sched/signal.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/gcd.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+
+#include "fsl_easrc.h"
+#include "imx-pcm.h"
+
+#define FSL_EASRC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_U24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_U20_3LE | \
+ SNDRV_PCM_FMTBIT_FLOAT_LE)
+
+static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(comp);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval = ucontrol->value.integer.value[0];
+
+ easrc_priv->bps_iec958[mc->regbase] = regval;
+
+ return 0;
+}
+
+static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_asrc *easrc = snd_soc_component_get_drvdata(comp);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+
+ ucontrol->value.enumerated.item[0] = easrc_priv->bps_iec958[mc->regbase];
+
+ return 0;
+}
+
+static int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval;
+ int ret;
+
+ ret = snd_soc_component_read(component, mc->regbase, ®val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = regval;
+
+ return 0;
+}
+
+static int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regval = ucontrol->value.integer.value[0];
+ int ret;
+
+ ret = snd_soc_component_write(component, mc->regbase, regval);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define SOC_SINGLE_REG_RW(xname, xreg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \
+ .put = fsl_easrc_set_reg, \
+ .private_value = (unsigned long)&(struct soc_mreg_control) \
+ { .regbase = xreg, .regcount = 1, .nbits = 32, \
+ .invert = 0, .min = 0, .max = 0xffffffff, } }
+
+#define SOC_SINGLE_VAL_RW(xname, xreg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = snd_soc_info_xr_sx, .get = fsl_easrc_iec958_get_bits, \
+ .put = fsl_easrc_iec958_put_bits, \
+ .private_value = (unsigned long)&(struct soc_mreg_control) \
+ { .regbase = xreg, .regcount = 1, .nbits = 32, \
+ .invert = 0, .min = 0, .max = 2, } }
+
+static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = {
+ SOC_SINGLE("Context 0 Dither Switch", REG_EASRC_COC(0), 0, 1, 0),
+ SOC_SINGLE("Context 1 Dither Switch", REG_EASRC_COC(1), 0, 1, 0),
+ SOC_SINGLE("Context 2 Dither Switch", REG_EASRC_COC(2), 0, 1, 0),
+ SOC_SINGLE("Context 3 Dither Switch", REG_EASRC_COC(3), 0, 1, 0),
+
+ SOC_SINGLE("Context 0 IEC958 Validity", REG_EASRC_COC(0), 2, 1, 0),
+ SOC_SINGLE("Context 1 IEC958 Validity", REG_EASRC_COC(1), 2, 1, 0),
+ SOC_SINGLE("Context 2 IEC958 Validity", REG_EASRC_COC(2), 2, 1, 0),
+ SOC_SINGLE("Context 3 IEC958 Validity", REG_EASRC_COC(3), 2, 1, 0),
+
+ SOC_SINGLE_VAL_RW("Context 0 IEC958 Bits Per Sample", 0),
+ SOC_SINGLE_VAL_RW("Context 1 IEC958 Bits Per Sample", 1),
+ SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2),
+ SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3),
+
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)),
+ SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)),
+ SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)),
+ SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)),
+ SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)),
+};
+
+/*
+ * fsl_easrc_set_rs_ratio
+ *
+ * According to the resample taps, calculate the resample ratio
+ * ratio = in_rate / out_rate
+ */
+static int fsl_easrc_set_rs_ratio(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ unsigned int in_rate = ctx_priv->in_params.norm_rate;
+ unsigned int out_rate = ctx_priv->out_params.norm_rate;
+ unsigned int int_bits;
+ unsigned int frac_bits;
+ u64 val;
+ u32 *r;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ int_bits = 5;
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ int_bits = 6;
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ int_bits = 7;
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (u64)in_rate << frac_bits;
+ do_div(val, out_rate);
+ r = (uint32_t *)&val;
+
+ if (r[1] & 0xFFFFF000) {
+ dev_err(&easrc->pdev->dev, "ratio exceed range\n");
+ return -EINVAL;
+ }
+
+ regmap_write(easrc->regmap, REG_EASRC_RRL(ctx->index),
+ EASRC_RRL_RS_RL(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_RRH(ctx->index),
+ EASRC_RRH_RS_RH(r[1]));
+
+ return 0;
+}
+
+/* Normalize input and output sample rates */
+static void fsl_easrc_normalize_rates(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ int a, b;
+
+ if (!ctx)
+ return;
+
+ ctx_priv = ctx->private;
+
+ a = ctx_priv->in_params.sample_rate;
+ b = ctx_priv->out_params.sample_rate;
+
+ a = gcd(a, b);
+
+ /* Divide by gcd to normalize the rate */
+ ctx_priv->in_params.norm_rate = ctx_priv->in_params.sample_rate / a;
+ ctx_priv->out_params.norm_rate = ctx_priv->out_params.sample_rate / a;
+}
+
+/* Resets the pointer of the coeff memory pointers */
+static int fsl_easrc_coeff_mem_ptr_reset(struct fsl_asrc *easrc,
+ unsigned int ctx_id, int mem_type)
+{
+ struct device *dev;
+ u32 reg, mask, val;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ switch (mem_type) {
+ case EASRC_PF_COEFF_MEM:
+ /* This resets the prefilter memory pointer addr */
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ reg = REG_EASRC_CCE1(ctx_id);
+ mask = EASRC_CCE1_COEF_MEM_RST_MASK;
+ val = EASRC_CCE1_COEF_MEM_RST;
+ break;
+ case EASRC_RS_COEFF_MEM:
+ /* This resets the resampling memory pointer addr */
+ reg = REG_EASRC_CRCC;
+ mask = EASRC_CRCC_RS_CPR_MASK;
+ val = EASRC_CRCC_RS_CPR;
+ break;
+ default:
+ dev_err(dev, "Unknown memory type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * To reset the write pointer back to zero, the register field
+ * ASRC_CTX_CTRL_EXT1x[PF_COEFF_MEM_RST] can be toggled from
+ * 0x0 to 0x1 to 0x0.
+ */
+ regmap_update_bits(easrc->regmap, reg, mask, 0);
+ regmap_update_bits(easrc->regmap, reg, mask, val);
+ regmap_update_bits(easrc->regmap, reg, mask, 0);
+
+ return 0;
+}
+
+static inline uint32_t bits_taps_to_val(unsigned int t)
+{
+ switch (t) {
+ case EASRC_RS_32_TAPS:
+ return 32;
+ case EASRC_RS_64_TAPS:
+ return 64;
+ case EASRC_RS_128_TAPS:
+ return 128;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_resampler_config(struct fsl_asrc *easrc)
+{
+ struct device *dev = &easrc->pdev->dev;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct asrc_firmware_hdr *hdr = easrc_priv->firmware_hdr;
+ struct interp_params *interp = easrc_priv->interp;
+ struct interp_params *selected_interp = NULL;
+ unsigned int num_coeff;
+ unsigned int i;
+ u64 *coef;
+ u32 *r;
+ int ret;
+
+ if (!hdr) {
+ dev_err(dev, "firmware not loaded!\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < hdr->interp_scen; i++) {
+ if ((interp[i].num_taps - 1) !=
+ bits_taps_to_val(easrc_priv->rs_num_taps))
+ continue;
+
+ coef = interp[i].coeff;
+ selected_interp = &interp[i];
+ dev_dbg(dev, "Selected interp_filter: %u taps - %u phases\n",
+ selected_interp->num_taps,
+ selected_interp->num_phases);
+ break;
+ }
+
+ if (!selected_interp) {
+ dev_err(dev, "failed to get interpreter configuration\n");
+ return -EINVAL;
+ }
+
+ /*
+ * RS_LOW - first half of center tap of the sinc function
+ * RS_HIGH - second half of center tap of the sinc function
+ * This is due to the fact the resampling function must be
+ * symetrical - i.e. odd number of taps
+ */
+ r = (uint32_t *)&selected_interp->center_tap;
+ regmap_write(easrc->regmap, REG_EASRC_RCTCL, EASRC_RCTCL_RS_CL(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_RCTCH, EASRC_RCTCH_RS_CH(r[1]));
+
+ /*
+ * Write Number of Resampling Coefficient Taps
+ * 00b - 32-Tap Resampling Filter
+ * 01b - 64-Tap Resampling Filter
+ * 10b - 128-Tap Resampling Filter
+ * 11b - N/A
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CRCC,
+ EASRC_CRCC_RS_TAPS_MASK,
+ EASRC_CRCC_RS_TAPS(easrc_priv->rs_num_taps));
+
+ /* Reset prefilter coefficient pointer back to 0 */
+ ret = fsl_easrc_coeff_mem_ptr_reset(easrc, 0, EASRC_RS_COEFF_MEM);
+ if (ret)
+ return ret;
+
+ /*
+ * When the filter is programmed to run in:
+ * 32-tap mode, 16-taps, 128-phases 4-coefficients per phase
+ * 64-tap mode, 32-taps, 64-phases 4-coefficients per phase
+ * 128-tap mode, 64-taps, 32-phases 4-coefficients per phase
+ * This means the number of writes is constant no matter
+ * the mode we are using
+ */
+ num_coeff = 16 * 128 * 4;
+
+ for (i = 0; i < num_coeff; i++) {
+ r = (uint32_t *)&coef[i];
+ regmap_write(easrc->regmap, REG_EASRC_CRCM,
+ EASRC_CRCM_RS_CWD(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_CRCM,
+ EASRC_CRCM_RS_CWD(r[1]));
+ }
+
+ return 0;
+}
+
+/**
+ * Scale filter coefficients (64 bits float)
+ * For input float32 normalized range (1.0,-1.0) -> output int[16,24,32]:
+ * scale it by multiplying filter coefficients by 2^31
+ * For input int[16, 24, 32] -> output float32
+ * scale it by multiplying filter coefficients by 2^-15, 2^-23, 2^-31
+ * input:
+ * asrc: Structure pointer of fsl_asrc
+ * infilter : Pointer to non-scaled input filter
+ * shift: The multiply factor
+ * output:
+ * outfilter: scaled filter
+ */
+static int fsl_easrc_normalize_filter(struct fsl_asrc *easrc,
+ u64 *infilter,
+ u64 *outfilter,
+ int shift)
+{
+ struct device *dev = &easrc->pdev->dev;
+ u64 coef = *infilter;
+ s64 exp = (coef & 0x7ff0000000000000ll) >> 52;
+ u64 outcoef;
+
+ /*
+ * If exponent is zero (value == 0), or 7ff (value == NaNs)
+ * dont touch the content
+ */
+ if (exp == 0 || exp == 0x7ff) {
+ *outfilter = coef;
+ return 0;
+ }
+
+ /* coef * 2^shift ==> exp + shift */
+ exp += shift;
+
+ if ((shift > 0 && exp >= 2047) ||
+ (shift < 0 && exp <= 0)) {
+ dev_err(dev, "coef out of range\n");
+ return -EINVAL;
+ }
+
+ outcoef = (u64)(coef & 0x800FFFFFFFFFFFFFll) + ((u64)exp << 52);
+ *outfilter = outcoef;
+
+ return 0;
+}
+
+static int fsl_easrc_write_pf_coeff_mem(struct fsl_asrc *easrc, int ctx_id,
+ u64 *coef, int n_taps, int shift)
+{
+ struct device *dev = &easrc->pdev->dev;
+ int ret = 0;
+ int i;
+ u32 *r;
+ u64 tmp;
+
+ /* If STx_NUM_TAPS is set to 0x0 then return */
+ if (!n_taps)
+ return 0;
+
+ if (!coef) {
+ dev_err(dev, "coef table is NULL\n");
+ return -EINVAL;
+ }
+
+ /*
+ * When switching between stages, the address pointer
+ * should be reset back to 0x0 before performing a write
+ */
+ ret = fsl_easrc_coeff_mem_ptr_reset(easrc, ctx_id, EASRC_PF_COEFF_MEM);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < (n_taps + 1) / 2; i++) {
+ ret = fsl_easrc_normalize_filter(easrc, &coef[i], &tmp, shift);
+ if (ret)
+ return ret;
+
+ r = (uint32_t *)&tmp;
+ regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+ EASRC_PCF_CD(r[0]));
+ regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+ EASRC_PCF_CD(r[1]));
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_prefilter_config(struct fsl_asrc *easrc,
+ unsigned int ctx_id)
+{
+ struct prefil_params *prefil, *selected_prefil = NULL;
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_easrc_priv *easrc_priv;
+ struct asrc_firmware_hdr *hdr;
+ struct fsl_asrc_pair *ctx;
+ struct device *dev;
+ u32 inrate, outrate, offset = 0;
+ u32 in_s_rate, out_s_rate, in_s_fmt, out_s_fmt;
+ int ret, i;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ easrc_priv = easrc->private;
+
+ ctx = easrc->pair[ctx_id];
+ ctx_priv = ctx->private;
+
+ in_s_rate = ctx_priv->in_params.sample_rate;
+ out_s_rate = ctx_priv->out_params.sample_rate;
+ in_s_fmt = ctx_priv->in_params.sample_format;
+ out_s_fmt = ctx_priv->out_params.sample_format;
+
+ ctx_priv->in_filled_sample = bits_taps_to_val(easrc_priv->rs_num_taps) / 2;
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample * out_s_rate / in_s_rate;
+
+ ctx_priv->st1_num_taps = 0;
+ ctx_priv->st2_num_taps = 0;
+
+ regmap_write(easrc->regmap, REG_EASRC_CCE1(ctx_id), 0);
+ regmap_write(easrc->regmap, REG_EASRC_CCE2(ctx_id), 0);
+
+ /*
+ * The audio float point data range is (-1, 1), the asrc would output
+ * all zero for float point input and integer output case, that is to
+ * drop the fractional part of the data directly.
+ *
+ * In order to support float to int conversion or int to float
+ * conversion we need to do special operation on the coefficient to
+ * enlarge/reduce the data to the expected range.
+ *
+ * For float to int case:
+ * Up sampling:
+ * 1. Create a 1 tap filter with center tap (only tap) of 2^31
+ * in 64 bits floating point.
+ * double value = (double)(((uint64_t)1) << 31)
+ * 2. Program 1 tap prefilter with center tap above.
+ *
+ * Down sampling,
+ * 1. If the filter is single stage filter, add "shift" to the exponent
+ * of stage 1 coefficients.
+ * 2. If the filter is two stage filter , add "shift" to the exponent
+ * of stage 2 coefficients.
+ *
+ * The "shift" is 31, same for int16, int24, int32 case.
+ *
+ * For int to float case:
+ * Up sampling:
+ * 1. Create a 1 tap filter with center tap (only tap) of 2^-31
+ * in 64 bits floating point.
+ * 2. Program 1 tap prefilter with center tap above.
+ *
+ * Down sampling,
+ * 1. If the filter is single stage filter, subtract "shift" to the
+ * exponent of stage 1 coefficients.
+ * 2. If the filter is two stage filter , subtract "shift" to the
+ * exponent of stage 2 coefficients.
+ *
+ * The "shift" is 15,23,31, different for int16, int24, int32 case.
+ *
+ */
+ if (out_s_rate >= in_s_rate) {
+ if (out_s_rate == in_s_rate)
+ regmap_update_bits(easrc->regmap,
+ REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_RS_BYPASS_MASK,
+ EASRC_CCE1_RS_BYPASS);
+
+ ctx_priv->st1_num_taps = 1;
+ ctx_priv->st1_coeff = &easrc_priv->const_coeff;
+ ctx_priv->st1_num_exp = 1;
+ ctx_priv->st2_num_taps = 0;
+
+ if (in_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE)
+ ctx_priv->st1_addexp = 31;
+ else if (in_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE)
+ ctx_priv->st1_addexp -= ctx_priv->in_params.fmt.addexp;
+ } else {
+ inrate = ctx_priv->in_params.norm_rate;
+ outrate = ctx_priv->out_params.norm_rate;
+
+ hdr = easrc_priv->firmware_hdr;
+ prefil = easrc_priv->prefil;
+
+ for (i = 0; i < hdr->prefil_scen; i++) {
+ if (inrate == prefil[i].insr &&
+ outrate == prefil[i].outsr) {
+ selected_prefil = &prefil[i];
+ dev_dbg(dev, "Selected prefilter: %u insr, %u outsr, %u st1_taps, %u st2_taps\n",
+ selected_prefil->insr,
+ selected_prefil->outsr,
+ selected_prefil->st1_taps,
+ selected_prefil->st2_taps);
+ break;
+ }
+ }
+
+ if (!selected_prefil) {
+ dev_err(dev, "Conversion from in ratio %u(%u) to out ratio %u(%u) is not supported\n",
+ in_s_rate, inrate,
+ out_s_rate, outrate);
+ return -EINVAL;
+ }
+
+ /*
+ * In prefilter coeff array, first st1_num_taps represent the
+ * stage1 prefilter coefficients followed by next st2_num_taps
+ * representing stage 2 coefficients
+ */
+ ctx_priv->st1_num_taps = selected_prefil->st1_taps;
+ ctx_priv->st1_coeff = selected_prefil->coeff;
+ ctx_priv->st1_num_exp = selected_prefil->st1_exp;
+
+ offset = ((selected_prefil->st1_taps + 1) / 2);
+ ctx_priv->st2_num_taps = selected_prefil->st2_taps;
+ ctx_priv->st2_coeff = selected_prefil->coeff + offset;
+
+ if (in_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE) {
+ /* only change stage2 coefficient for 2 stage case */
+ if (ctx_priv->st2_num_taps > 0)
+ ctx_priv->st2_addexp = 31;
+ else
+ ctx_priv->st1_addexp = 31;
+ } else if (in_s_fmt != SNDRV_PCM_FORMAT_FLOAT_LE &&
+ out_s_fmt == SNDRV_PCM_FORMAT_FLOAT_LE) {
+ if (ctx_priv->st2_num_taps > 0)
+ ctx_priv->st2_addexp -= ctx_priv->in_params.fmt.addexp;
+ else
+ ctx_priv->st1_addexp -= ctx_priv->in_params.fmt.addexp;
+ }
+ }
+
+ ctx_priv->in_filled_sample += (ctx_priv->st1_num_taps / 2) * ctx_priv->st1_num_exp +
+ ctx_priv->st2_num_taps / 2;
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample * out_s_rate / in_s_rate;
+
+ if (ctx_priv->in_filled_sample * out_s_rate % in_s_rate != 0)
+ ctx_priv->out_missed_sample += 1;
+ /*
+ * To modify the value of a prefilter coefficient, the user must
+ * perform a write to the register ASRC_PRE_COEFF_FIFOn[COEFF_DATA]
+ * while the respective context RUN_EN bit is set to 0b0
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_EN_MASK, 0);
+
+ if (ctx_priv->st1_num_taps > EASRC_MAX_PF_TAPS) {
+ dev_err(dev, "ST1 taps [%d] mus be lower than %d\n",
+ ctx_priv->st1_num_taps, EASRC_MAX_PF_TAPS);
+ ret = -EINVAL;
+ goto ctx_error;
+ }
+
+ /* Update ctx ST1_NUM_TAPS in Context Control Extended 2 register */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+ EASRC_CCE2_ST1_TAPS_MASK,
+ EASRC_CCE2_ST1_TAPS(ctx_priv->st1_num_taps - 1));
+
+ /* Prefilter Coefficient Write Select to write in ST1 coeff */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_COEF_WS_MASK,
+ EASRC_PF_ST1_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, ctx_id,
+ ctx_priv->st1_coeff,
+ ctx_priv->st1_num_taps,
+ ctx_priv->st1_addexp);
+ if (ret)
+ goto ctx_error;
+
+ if (ctx_priv->st2_num_taps > 0) {
+ if (ctx_priv->st2_num_taps + ctx_priv->st1_num_taps > EASRC_MAX_PF_TAPS) {
+ dev_err(dev, "ST2 taps [%d] mus be lower than %d\n",
+ ctx_priv->st2_num_taps, EASRC_MAX_PF_TAPS);
+ ret = -EINVAL;
+ goto ctx_error;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_TSEN_MASK,
+ EASRC_CCE1_PF_TSEN);
+ /*
+ * Enable prefilter stage1 writeback floating point
+ * which is used for FLOAT_LE case
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_ST1_WBFP_MASK,
+ EASRC_CCE1_PF_ST1_WBFP);
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_EXP_MASK,
+ EASRC_CCE1_PF_EXP(ctx_priv->st1_num_exp - 1));
+
+ /* Update ctx ST2_NUM_TAPS in Context Control Extended 2 reg */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+ EASRC_CCE2_ST2_TAPS_MASK,
+ EASRC_CCE2_ST2_TAPS(ctx_priv->st2_num_taps - 1));
+
+ /* Prefilter Coefficient Write Select to write in ST2 coeff */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_COEF_WS_MASK,
+ EASRC_PF_ST2_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, ctx_id,
+ ctx_priv->st2_coeff,
+ ctx_priv->st2_num_taps,
+ ctx_priv->st2_addexp);
+ if (ret)
+ goto ctx_error;
+ }
+
+ return 0;
+
+ctx_error:
+ return ret;
+}
+
+static int fsl_easrc_max_ch_for_slot(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_slot *slot)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ int st1_mem_alloc = 0, st2_mem_alloc = 0;
+ int pf_mem_alloc = 0;
+ int max_channels = 8 - slot->num_channel;
+ int channels = 0;
+
+ if (ctx_priv->st1_num_taps > 0) {
+ if (ctx_priv->st2_num_taps > 0)
+ st1_mem_alloc =
+ (ctx_priv->st1_num_taps - 1) * ctx_priv->st1_num_exp + 1;
+ else
+ st1_mem_alloc = ctx_priv->st1_num_taps;
+ }
+
+ if (ctx_priv->st2_num_taps > 0)
+ st2_mem_alloc = ctx_priv->st2_num_taps;
+
+ pf_mem_alloc = st1_mem_alloc + st2_mem_alloc;
+
+ if (pf_mem_alloc != 0)
+ channels = (6144 - slot->pf_mem_used) / pf_mem_alloc;
+ else
+ channels = 8;
+
+ if (channels < max_channels)
+ max_channels = channels;
+
+ return max_channels;
+}
+
+static int fsl_easrc_config_one_slot(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_slot *slot,
+ unsigned int slot_ctx_idx,
+ unsigned int *req_channels,
+ unsigned int *start_channel,
+ unsigned int *avail_channel)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ int st1_chanxexp, st1_mem_alloc = 0, st2_mem_alloc = 0;
+ unsigned int reg0, reg1, reg2, reg3;
+ unsigned int addr;
+
+ if (slot->slot_index == 0) {
+ reg0 = REG_EASRC_DPCS0R0(slot_ctx_idx);
+ reg1 = REG_EASRC_DPCS0R1(slot_ctx_idx);
+ reg2 = REG_EASRC_DPCS0R2(slot_ctx_idx);
+ reg3 = REG_EASRC_DPCS0R3(slot_ctx_idx);
+ } else {
+ reg0 = REG_EASRC_DPCS1R0(slot_ctx_idx);
+ reg1 = REG_EASRC_DPCS1R1(slot_ctx_idx);
+ reg2 = REG_EASRC_DPCS1R2(slot_ctx_idx);
+ reg3 = REG_EASRC_DPCS1R3(slot_ctx_idx);
+ }
+
+ if (*req_channels <= *avail_channel) {
+ slot->num_channel = *req_channels;
+ *req_channels = 0;
+ } else {
+ slot->num_channel = *avail_channel;
+ *req_channels -= *avail_channel;
+ }
+
+ slot->min_channel = *start_channel;
+ slot->max_channel = *start_channel + slot->num_channel - 1;
+ slot->ctx_index = ctx->index;
+ slot->busy = true;
+ *start_channel += slot->num_channel;
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_MAXCH_MASK,
+ EASRC_DPCS0R0_MAXCH(slot->max_channel));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_MINCH_MASK,
+ EASRC_DPCS0R0_MINCH(slot->min_channel));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_NUMCH_MASK,
+ EASRC_DPCS0R0_NUMCH(slot->num_channel - 1));
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_CTXNUM_MASK,
+ EASRC_DPCS0R0_CTXNUM(slot->ctx_index));
+
+ if (ctx_priv->st1_num_taps > 0) {
+ if (ctx_priv->st2_num_taps > 0)
+ st1_mem_alloc =
+ (ctx_priv->st1_num_taps - 1) * slot->num_channel *
+ ctx_priv->st1_num_exp + slot->num_channel;
+ else
+ st1_mem_alloc = ctx_priv->st1_num_taps * slot->num_channel;
+
+ slot->pf_mem_used = st1_mem_alloc;
+ regmap_update_bits(easrc->regmap, reg2,
+ EASRC_DPCS0R2_ST1_MA_MASK,
+ EASRC_DPCS0R2_ST1_MA(st1_mem_alloc));
+
+ if (slot->slot_index == 1)
+ addr = PREFILTER_MEM_LEN - st1_mem_alloc;
+ else
+ addr = 0;
+
+ regmap_update_bits(easrc->regmap, reg2,
+ EASRC_DPCS0R2_ST1_SA_MASK,
+ EASRC_DPCS0R2_ST1_SA(addr));
+ }
+
+ if (ctx_priv->st2_num_taps > 0) {
+ st1_chanxexp = slot->num_channel * (ctx_priv->st1_num_exp - 1);
+
+ regmap_update_bits(easrc->regmap, reg1,
+ EASRC_DPCS0R1_ST1_EXP_MASK,
+ EASRC_DPCS0R1_ST1_EXP(st1_chanxexp));
+
+ st2_mem_alloc = slot->num_channel * ctx_priv->st2_num_taps;
+ slot->pf_mem_used += st2_mem_alloc;
+ regmap_update_bits(easrc->regmap, reg3,
+ EASRC_DPCS0R3_ST2_MA_MASK,
+ EASRC_DPCS0R3_ST2_MA(st2_mem_alloc));
+
+ if (slot->slot_index == 1)
+ addr = PREFILTER_MEM_LEN - st1_mem_alloc - st2_mem_alloc;
+ else
+ addr = st1_mem_alloc;
+
+ regmap_update_bits(easrc->regmap, reg3,
+ EASRC_DPCS0R3_ST2_SA_MASK,
+ EASRC_DPCS0R3_ST2_SA(addr));
+ }
+
+ regmap_update_bits(easrc->regmap, reg0,
+ EASRC_DPCS0R0_EN_MASK, EASRC_DPCS0R0_EN);
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_config_slot
+ *
+ * A single context can be split amongst any of the 4 context processing pipes
+ * in the design.
+ * The total number of channels consumed within the context processor must be
+ * less than or equal to 8. if a single context is configured to contain more
+ * than 8 channels then it must be distributed across multiple context
+ * processing pipe slots.
+ *
+ */
+static int fsl_easrc_config_slot(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_asrc_pair *ctx = easrc->pair[ctx_id];
+ int req_channels = ctx->channels;
+ int start_channel = 0, avail_channel;
+ struct fsl_easrc_slot *slot0, *slot1;
+ struct fsl_easrc_slot *slota, *slotb;
+ int i, ret;
+
+ if (req_channels <= 0)
+ return -EINVAL;
+
+ for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+ slot0 = &easrc_priv->slot[i][0];
+ slot1 = &easrc_priv->slot[i][1];
+
+ if (slot0->busy && slot1->busy) {
+ continue;
+ } else if ((slot0->busy && slot0->ctx_index == ctx->index) ||
+ (slot1->busy && slot1->ctx_index == ctx->index)) {
+ continue;
+ } else if (!slot0->busy) {
+ slota = slot0;
+ slotb = slot1;
+ slota->slot_index = 0;
+ } else if (!slot1->busy) {
+ slota = slot1;
+ slotb = slot0;
+ slota->slot_index = 1;
+ }
+
+ if (!slota || !slotb)
+ continue;
+
+ avail_channel = fsl_easrc_max_ch_for_slot(ctx, slotb);
+ if (avail_channel <= 0)
+ continue;
+
+ ret = fsl_easrc_config_one_slot(ctx, slota, i, &req_channels,
+ &start_channel, &avail_channel);
+ if (ret)
+ return ret;
+
+ if (req_channels > 0)
+ continue;
+ else
+ break;
+ }
+
+ if (req_channels > 0) {
+ dev_err(&easrc->pdev->dev, "no avail slot.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_release_slot
+ *
+ * Clear the slot configuration
+ */
+static int fsl_easrc_release_slot(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_asrc_pair *ctx = easrc->pair[ctx_id];
+ int i;
+
+ for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+ if (easrc_priv->slot[i][0].busy &&
+ easrc_priv->slot[i][0].ctx_index == ctx->index) {
+ easrc_priv->slot[i][0].busy = false;
+ easrc_priv->slot[i][0].num_channel = 0;
+ easrc_priv->slot[i][0].pf_mem_used = 0;
+ /* set registers */
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R0(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R1(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R2(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS0R3(i), 0);
+ }
+
+ if (easrc_priv->slot[i][1].busy &&
+ easrc_priv->slot[i][1].ctx_index == ctx->index) {
+ easrc_priv->slot[i][1].busy = false;
+ easrc_priv->slot[i][1].num_channel = 0;
+ easrc_priv->slot[i][1].pf_mem_used = 0;
+ /* set registers */
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R0(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R1(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R2(i), 0);
+ regmap_write(easrc->regmap, REG_EASRC_DPCS1R3(i), 0);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * fsl_easrc_config_context
+ *
+ * Configure the register relate with context.
+ */
+int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_asrc_pair *ctx;
+ struct device *dev;
+ unsigned long lock_flags;
+ int ret;
+
+ if (!easrc)
+ return -ENODEV;
+
+ dev = &easrc->pdev->dev;
+
+ if (ctx_id >= EASRC_CTX_MAX_NUM) {
+ dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+ return -EINVAL;
+ }
+
+ ctx = easrc->pair[ctx_id];
+
+ ctx_priv = ctx->private;
+
+ fsl_easrc_normalize_rates(ctx);
+
+ ret = fsl_easrc_set_rs_ratio(ctx);
+ if (ret)
+ return ret;
+
+ /* Initialize the context coeficients */
+ ret = fsl_easrc_prefilter_config(easrc, ctx->index);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ ret = fsl_easrc_config_slot(easrc, ctx->index);
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+ if (ret)
+ return ret;
+
+ /*
+ * Both prefilter and resampling filters can use following
+ * initialization modes:
+ * 2 - zero-fil mode
+ * 1 - replication mode
+ * 0 - software control
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_RS_INIT_MASK,
+ EASRC_CCE1_RS_INIT(ctx_priv->rs_init_mode));
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+ EASRC_CCE1_PF_INIT_MASK,
+ EASRC_CCE1_PF_INIT(ctx_priv->pf_init_mode));
+
+ /*
+ * Context Input FIFO Watermark
+ * DMA request is generated when input FIFO < FIFO_WTMK
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_FIFO_WTMK_MASK,
+ EASRC_CC_FIFO_WTMK(ctx_priv->in_params.fifo_wtmk));
+
+ /*
+ * Context Output FIFO Watermark
+ * DMA request is generated when output FIFO > FIFO_WTMK
+ * So we set fifo_wtmk -1 to register.
+ */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx_id),
+ EASRC_COC_FIFO_WTMK_MASK,
+ EASRC_COC_FIFO_WTMK(ctx_priv->out_params.fifo_wtmk - 1));
+
+ /* Number of channels */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+ EASRC_CC_CHEN_MASK,
+ EASRC_CC_CHEN(ctx->channels - 1));
+ return 0;
+}
+
+static int fsl_easrc_process_format(struct fsl_asrc_pair *ctx,
+ struct fsl_easrc_data_fmt *fmt,
+ snd_pcm_format_t raw_fmt)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ int ret;
+
+ if (!fmt)
+ return -EINVAL;
+
+ /*
+ * Context Input Floating Point Format
+ * 0 - Integer Format
+ * 1 - Single Precision FP Format
+ */
+ fmt->floating_point = !snd_pcm_format_linear(raw_fmt);
+ fmt->sample_pos = 0;
+ fmt->iec958 = 0;
+
+ /* Get the data width */
+ switch (snd_pcm_format_width(raw_fmt)) {
+ case 16:
+ fmt->width = EASRC_WIDTH_16_BIT;
+ fmt->addexp = 15;
+ break;
+ case 20:
+ fmt->width = EASRC_WIDTH_20_BIT;
+ fmt->addexp = 19;
+ break;
+ case 24:
+ fmt->width = EASRC_WIDTH_24_BIT;
+ fmt->addexp = 23;
+ break;
+ case 32:
+ fmt->width = EASRC_WIDTH_32_BIT;
+ fmt->addexp = 31;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (raw_fmt) {
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ fmt->width = easrc_priv->bps_iec958[ctx->index];
+ fmt->iec958 = 1;
+ fmt->floating_point = 0;
+ if (fmt->width == EASRC_WIDTH_16_BIT) {
+ fmt->sample_pos = 12;
+ fmt->addexp = 15;
+ } else if (fmt->width == EASRC_WIDTH_20_BIT) {
+ fmt->sample_pos = 8;
+ fmt->addexp = 19;
+ } else if (fmt->width == EASRC_WIDTH_24_BIT) {
+ fmt->sample_pos = 4;
+ fmt->addexp = 23;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Data Endianness
+ * 0 - Little-Endian
+ * 1 - Big-Endian
+ */
+ ret = snd_pcm_format_big_endian(raw_fmt);
+ if (ret < 0)
+ return ret;
+
+ fmt->endianness = ret;
+
+ /*
+ * Input Data sign
+ * 0b - Signed Format
+ * 1b - Unsigned Format
+ */
+ fmt->unsign = snd_pcm_format_unsigned(raw_fmt) > 0 ? 1 : 0;
+
+ return 0;
+}
+
+int fsl_easrc_set_ctx_format(struct fsl_asrc_pair *ctx,
+ snd_pcm_format_t *in_raw_format,
+ snd_pcm_format_t *out_raw_format)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ struct fsl_easrc_data_fmt *in_fmt = &ctx_priv->in_params.fmt;
+ struct fsl_easrc_data_fmt *out_fmt = &ctx_priv->out_params.fmt;
+ int ret;
+
+ /* Get the bitfield values for input data format */
+ if (in_raw_format && out_raw_format) {
+ ret = fsl_easrc_process_format(ctx, in_fmt, *in_raw_format);
+ if (ret)
+ return ret;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_BPS_MASK,
+ EASRC_CC_BPS(in_fmt->width));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_ENDIANNESS_MASK,
+ in_fmt->endianness << EASRC_CC_ENDIANNESS_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FMT_MASK,
+ in_fmt->floating_point << EASRC_CC_FMT_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_INSIGN_MASK,
+ in_fmt->unsign << EASRC_CC_INSIGN_SHIFT);
+
+ /* In Sample Position */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_SAMPLE_POS_MASK,
+ EASRC_CC_SAMPLE_POS(in_fmt->sample_pos));
+
+ /* Get the bitfield values for input data format */
+ if (in_raw_format && out_raw_format) {
+ ret = fsl_easrc_process_format(ctx, out_fmt, *out_raw_format);
+ if (ret)
+ return ret;
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_BPS_MASK,
+ EASRC_COC_BPS(out_fmt->width));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_ENDIANNESS_MASK,
+ out_fmt->endianness << EASRC_COC_ENDIANNESS_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FMT_MASK,
+ out_fmt->floating_point << EASRC_COC_FMT_SHIFT);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_OUTSIGN_MASK,
+ out_fmt->unsign << EASRC_COC_OUTSIGN_SHIFT);
+
+ /* Out Sample Position */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_SAMPLE_POS_MASK,
+ EASRC_COC_SAMPLE_POS(out_fmt->sample_pos));
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_IEC_EN_MASK,
+ out_fmt->iec958 << EASRC_COC_IEC_EN_SHIFT);
+
+ return ret;
+}
+
+/*
+ * The ASRC provides interleaving support in hardware to ensure that a
+ * variety of sample sources can be internally combined
+ * to conform with this format. Interleaving parameters are accessed
+ * through the ASRC_CTRL_IN_ACCESSa and ASRC_CTRL_OUT_ACCESSa registers
+ */
+int fsl_easrc_set_ctx_organziation(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct device *dev;
+ struct fsl_asrc *easrc;
+
+ if (!ctx)
+ return -ENODEV;
+
+ easrc = ctx->asrc;
+ ctx_priv = ctx->private;
+ dev = &easrc->pdev->dev;
+
+ /* input interleaving parameters */
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_ITER_MASK,
+ EASRC_CIA_ITER(ctx_priv->in_params.iterations));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_GRLEN_MASK,
+ EASRC_CIA_GRLEN(ctx_priv->in_params.group_len));
+ regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+ EASRC_CIA_ACCLEN_MASK,
+ EASRC_CIA_ACCLEN(ctx_priv->in_params.access_len));
+
+ /* output interleaving parameters */
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_ITER_MASK,
+ EASRC_COA_ITER(ctx_priv->out_params.iterations));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_GRLEN_MASK,
+ EASRC_COA_GRLEN(ctx_priv->out_params.group_len));
+ regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+ EASRC_COA_ACCLEN_MASK,
+ EASRC_COA_ACCLEN(ctx_priv->out_params.access_len));
+
+ return 0;
+}
+
+/*
+ * Request one of the available contexts
+ *
+ * Returns a negative number on error and >=0 as context id
+ * on success
+ */
+int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx)
+{
+ enum asrc_pair_index index = ASRC_INVALID_PAIR;
+ struct fsl_asrc *easrc = ctx->asrc;
+ struct device *dev;
+ unsigned long lock_flags;
+ int ret = 0;
+ int i;
+
+ dev = &easrc->pdev->dev;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+
+ for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+ if (easrc->pair[i])
+ continue;
+
+ index = i;
+ break;
+ }
+
+ if (index == ASRC_INVALID_PAIR) {
+ dev_err(dev, "all contexts are busy\n");
+ ret = -EBUSY;
+ } else if (channels > easrc->channel_avail) {
+ dev_err(dev, "can't give the required channels: %d\n",
+ channels);
+ ret = -EINVAL;
+ } else {
+ ctx->index = index;
+ ctx->channels = channels;
+ easrc->pair[index] = ctx;
+ easrc->channel_avail -= channels;
+ }
+
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ return ret;
+}
+
+/*
+ * Release the context
+ *
+ * This funciton is mainly doing the revert thing in request context
+ */
+void fsl_easrc_release_context(struct fsl_asrc_pair *ctx)
+{
+ unsigned long lock_flags;
+ struct fsl_asrc *easrc;
+ struct device *dev;
+
+ if (!ctx)
+ return;
+
+ easrc = ctx->asrc;
+ dev = &easrc->pdev->dev;
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+
+ fsl_easrc_release_slot(easrc, ctx->index);
+
+ easrc->channel_avail += ctx->channels;
+ easrc->pair[ctx->index] = NULL;
+
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+}
+
+/*
+ * Start the context
+ *
+ * Enable the DMA request and context
+ */
+int fsl_easrc_start_context(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FWMDE_MASK, EASRC_CC_FWMDE);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FWMDE_MASK, EASRC_COC_FWMDE);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_EN_MASK, EASRC_CC_EN);
+ return 0;
+}
+
+/*
+ * Stop the context
+ *
+ * Disable the DMA request and context
+ */
+int fsl_easrc_stop_context(struct fsl_asrc_pair *ctx)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ int val, i;
+ int size = 0;
+ int retry = 200;
+
+ regmap_read(easrc->regmap, REG_EASRC_CC(ctx->index), &val);
+
+ if (val & EASRC_CC_EN_MASK) {
+ regmap_update_bits(easrc->regmap,
+ REG_EASRC_CC(ctx->index),
+ EASRC_CC_STOP_MASK, EASRC_CC_STOP);
+ do {
+ regmap_read(easrc->regmap, REG_EASRC_SFS(ctx->index), &val);
+ val &= EASRC_SFS_NSGO_MASK;
+ size = val >> EASRC_SFS_NSGO_SHIFT;
+
+ /* Read FIFO, drop the data */
+ for (i = 0; i < size * ctx->channels; i++)
+ regmap_read(easrc->regmap, REG_EASRC_RDFIFO(ctx->index), &val);
+ /* Check RUN_STOP_DONE */
+ regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+ if (val & EASRC_IRQF_RSD(1 << ctx->index)) {
+ /*Clear RUN_STOP_DONE*/
+ regmap_write_bits(easrc->regmap,
+ REG_EASRC_IRQF,
+ EASRC_IRQF_RSD(1 << ctx->index),
+ EASRC_IRQF_RSD(1 << ctx->index));
+ break;
+ }
+ udelay(100);
+ } while (--retry);
+
+ if (retry == 0)
+ dev_warn(&easrc->pdev->dev, "RUN STOP fail\n");
+ }
+
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_EN_MASK | EASRC_CC_STOP_MASK, 0);
+ regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+ EASRC_CC_FWMDE_MASK, 0);
+ regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+ EASRC_COC_FWMDE_MASK, 0);
+ return 0;
+}
+
+struct dma_chan *fsl_easrc_get_dma_channel(struct fsl_asrc_pair *ctx,
+ bool dir)
+{
+ struct fsl_asrc *easrc = ctx->asrc;
+ enum asrc_pair_index index = ctx->index;
+ char name[8];
+
+ /* Example of dma name: ctx0_rx */
+ sprintf(name, "ctx%c_%cx", index + '0', dir == IN ? 'r' : 't');
+
+ return dma_request_slave_channel(&easrc->pdev->dev, name);
+};
+EXPORT_SYMBOL_GPL(fsl_easrc_get_dma_channel);
+
+static const unsigned int easrc_rates[] = {
+ 8000, 11025, 12000, 16000,
+ 22050, 24000, 32000, 44100,
+ 48000, 64000, 88200, 96000,
+ 128000, 176400, 192000, 256000,
+ 352800, 384000, 705600, 768000,
+};
+
+static const struct snd_pcm_hw_constraint_list easrc_rate_constraints = {
+ .count = ARRAY_SIZE(easrc_rates),
+ .list = easrc_rates,
+};
+
+static int fsl_easrc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &easrc_rate_constraints);
+}
+
+static int fsl_easrc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = fsl_easrc_start_context(ctx);
+ if (ret)
+ return ret;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = fsl_easrc_stop_context(ctx);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *easrc = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = &easrc->pdev->dev;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ snd_pcm_format_t format = params_format(params);
+ int ret;
+
+ ret = fsl_easrc_request_context(channels, ctx);
+ if (ret) {
+ dev_err(dev, "failed to request context\n");
+ return ret;
+ }
+
+ ctx_priv->ctx_streams |= BIT(substream->stream);
+
+ /*
+ * Set the input and output ratio so we can compute
+ * the resampling ratio in RS_LOW/HIGH
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ctx_priv->in_params.sample_rate = rate;
+ ctx_priv->in_params.sample_format = format;
+ ctx_priv->out_params.sample_rate = easrc->asrc_rate;
+ ctx_priv->out_params.sample_format = easrc->asrc_format;
+ } else {
+ ctx_priv->out_params.sample_rate = rate;
+ ctx_priv->out_params.sample_format = format;
+ ctx_priv->in_params.sample_rate = easrc->asrc_rate;
+ ctx_priv->in_params.sample_format = easrc->asrc_format;
+ }
+
+ ctx->channels = channels;
+ ctx_priv->in_params.fifo_wtmk = 0x20;
+ ctx_priv->out_params.fifo_wtmk = 0x20;
+
+ /*
+ * Do only rate conversion and keep the same format for input
+ * and output data
+ */
+ ret = fsl_easrc_set_ctx_format(ctx,
+ &ctx_priv->in_params.sample_format,
+ &ctx_priv->out_params.sample_format);
+ if (ret) {
+ dev_err(dev, "failed to set format %d", ret);
+ return ret;
+ }
+
+ ret = fsl_easrc_config_context(easrc, ctx->index);
+ if (ret) {
+ dev_err(dev, "failed to config context\n");
+ return ret;
+ }
+
+ ctx_priv->in_params.iterations = 1;
+ ctx_priv->in_params.group_len = ctx->channels;
+ ctx_priv->in_params.access_len = ctx->channels;
+ ctx_priv->out_params.iterations = 1;
+ ctx_priv->out_params.group_len = ctx->channels;
+ ctx_priv->out_params.access_len = ctx->channels;
+
+ ret = fsl_easrc_set_ctx_organziation(ctx);
+ if (ret) {
+ dev_err(dev, "failed to set fifo organization\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *ctx = runtime->private_data;
+ struct fsl_easrc_ctx_priv *ctx_priv = ctx->private;
+
+ if (ctx && (ctx_priv->ctx_streams & BIT(substream->stream))) {
+ ctx_priv->ctx_streams &= ~BIT(substream->stream);
+ fsl_easrc_release_context(ctx);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops fsl_easrc_dai_ops = {
+ .startup = fsl_easrc_startup,
+ .trigger = fsl_easrc_trigger,
+ .hw_params = fsl_easrc_hw_params,
+ .hw_free = fsl_easrc_hw_free,
+};
+
+static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(cpu_dai->dev);
+
+ snd_soc_dai_init_dma_data(cpu_dai,
+ &easrc->dma_params_tx,
+ &easrc->dma_params_rx);
+ return 0;
+}
+
+static struct snd_soc_dai_driver fsl_easrc_dai = {
+ .probe = fsl_easrc_dai_probe,
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_easrc_component = {
+ .name = "fsl-easrc-dai",
+ .controls = fsl_easrc_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_easrc_snd_controls),
+};
+
+static const struct reg_default fsl_easrc_reg_defaults[] = {
+ {REG_EASRC_WRFIFO(0), 0x00000000},
+ {REG_EASRC_WRFIFO(1), 0x00000000},
+ {REG_EASRC_WRFIFO(2), 0x00000000},
+ {REG_EASRC_WRFIFO(3), 0x00000000},
+ {REG_EASRC_RDFIFO(0), 0x00000000},
+ {REG_EASRC_RDFIFO(1), 0x00000000},
+ {REG_EASRC_RDFIFO(2), 0x00000000},
+ {REG_EASRC_RDFIFO(3), 0x00000000},
+ {REG_EASRC_CC(0), 0x00000000},
+ {REG_EASRC_CC(1), 0x00000000},
+ {REG_EASRC_CC(2), 0x00000000},
+ {REG_EASRC_CC(3), 0x00000000},
+ {REG_EASRC_CCE1(0), 0x00000000},
+ {REG_EASRC_CCE1(1), 0x00000000},
+ {REG_EASRC_CCE1(2), 0x00000000},
+ {REG_EASRC_CCE1(3), 0x00000000},
+ {REG_EASRC_CCE2(0), 0x00000000},
+ {REG_EASRC_CCE2(1), 0x00000000},
+ {REG_EASRC_CCE2(2), 0x00000000},
+ {REG_EASRC_CCE2(3), 0x00000000},
+ {REG_EASRC_CIA(0), 0x00000000},
+ {REG_EASRC_CIA(1), 0x00000000},
+ {REG_EASRC_CIA(2), 0x00000000},
+ {REG_EASRC_CIA(3), 0x00000000},
+ {REG_EASRC_DPCS0R0(0), 0x00000000},
+ {REG_EASRC_DPCS0R0(1), 0x00000000},
+ {REG_EASRC_DPCS0R0(2), 0x00000000},
+ {REG_EASRC_DPCS0R0(3), 0x00000000},
+ {REG_EASRC_DPCS0R1(0), 0x00000000},
+ {REG_EASRC_DPCS0R1(1), 0x00000000},
+ {REG_EASRC_DPCS0R1(2), 0x00000000},
+ {REG_EASRC_DPCS0R1(3), 0x00000000},
+ {REG_EASRC_DPCS0R2(0), 0x00000000},
+ {REG_EASRC_DPCS0R2(1), 0x00000000},
+ {REG_EASRC_DPCS0R2(2), 0x00000000},
+ {REG_EASRC_DPCS0R2(3), 0x00000000},
+ {REG_EASRC_DPCS0R3(0), 0x00000000},
+ {REG_EASRC_DPCS0R3(1), 0x00000000},
+ {REG_EASRC_DPCS0R3(2), 0x00000000},
+ {REG_EASRC_DPCS0R3(3), 0x00000000},
+ {REG_EASRC_DPCS1R0(0), 0x00000000},
+ {REG_EASRC_DPCS1R0(1), 0x00000000},
+ {REG_EASRC_DPCS1R0(2), 0x00000000},
+ {REG_EASRC_DPCS1R0(3), 0x00000000},
+ {REG_EASRC_DPCS1R1(0), 0x00000000},
+ {REG_EASRC_DPCS1R1(1), 0x00000000},
+ {REG_EASRC_DPCS1R1(2), 0x00000000},
+ {REG_EASRC_DPCS1R1(3), 0x00000000},
+ {REG_EASRC_DPCS1R2(0), 0x00000000},
+ {REG_EASRC_DPCS1R2(1), 0x00000000},
+ {REG_EASRC_DPCS1R2(2), 0x00000000},
+ {REG_EASRC_DPCS1R2(3), 0x00000000},
+ {REG_EASRC_DPCS1R3(0), 0x00000000},
+ {REG_EASRC_DPCS1R3(1), 0x00000000},
+ {REG_EASRC_DPCS1R3(2), 0x00000000},
+ {REG_EASRC_DPCS1R3(3), 0x00000000},
+ {REG_EASRC_COC(0), 0x00000000},
+ {REG_EASRC_COC(1), 0x00000000},
+ {REG_EASRC_COC(2), 0x00000000},
+ {REG_EASRC_COC(3), 0x00000000},
+ {REG_EASRC_COA(0), 0x00000000},
+ {REG_EASRC_COA(1), 0x00000000},
+ {REG_EASRC_COA(2), 0x00000000},
+ {REG_EASRC_COA(3), 0x00000000},
+ {REG_EASRC_SFS(0), 0x00000000},
+ {REG_EASRC_SFS(1), 0x00000000},
+ {REG_EASRC_SFS(2), 0x00000000},
+ {REG_EASRC_SFS(3), 0x00000000},
+ {REG_EASRC_RRL(0), 0x00000000},
+ {REG_EASRC_RRL(1), 0x00000000},
+ {REG_EASRC_RRL(2), 0x00000000},
+ {REG_EASRC_RRL(3), 0x00000000},
+ {REG_EASRC_RRH(0), 0x00000000},
+ {REG_EASRC_RRH(1), 0x00000000},
+ {REG_EASRC_RRH(2), 0x00000000},
+ {REG_EASRC_RRH(3), 0x00000000},
+ {REG_EASRC_RUC(0), 0x00000000},
+ {REG_EASRC_RUC(1), 0x00000000},
+ {REG_EASRC_RUC(2), 0x00000000},
+ {REG_EASRC_RUC(3), 0x00000000},
+ {REG_EASRC_RUR(0), 0x7FFFFFFF},
+ {REG_EASRC_RUR(1), 0x7FFFFFFF},
+ {REG_EASRC_RUR(2), 0x7FFFFFFF},
+ {REG_EASRC_RUR(3), 0x7FFFFFFF},
+ {REG_EASRC_RCTCL, 0x00000000},
+ {REG_EASRC_RCTCH, 0x00000000},
+ {REG_EASRC_PCF(0), 0x00000000},
+ {REG_EASRC_PCF(1), 0x00000000},
+ {REG_EASRC_PCF(2), 0x00000000},
+ {REG_EASRC_PCF(3), 0x00000000},
+ {REG_EASRC_CRCM, 0x00000000},
+ {REG_EASRC_CRCC, 0x00000000},
+ {REG_EASRC_IRQC, 0x00000FFF},
+ {REG_EASRC_IRQF, 0x00000000},
+ {REG_EASRC_CS0(0), 0x00000000},
+ {REG_EASRC_CS0(1), 0x00000000},
+ {REG_EASRC_CS0(2), 0x00000000},
+ {REG_EASRC_CS0(3), 0x00000000},
+ {REG_EASRC_CS1(0), 0x00000000},
+ {REG_EASRC_CS1(1), 0x00000000},
+ {REG_EASRC_CS1(2), 0x00000000},
+ {REG_EASRC_CS1(3), 0x00000000},
+ {REG_EASRC_CS2(0), 0x00000000},
+ {REG_EASRC_CS2(1), 0x00000000},
+ {REG_EASRC_CS2(2), 0x00000000},
+ {REG_EASRC_CS2(3), 0x00000000},
+ {REG_EASRC_CS3(0), 0x00000000},
+ {REG_EASRC_CS3(1), 0x00000000},
+ {REG_EASRC_CS3(2), 0x00000000},
+ {REG_EASRC_CS3(3), 0x00000000},
+ {REG_EASRC_CS4(0), 0x00000000},
+ {REG_EASRC_CS4(1), 0x00000000},
+ {REG_EASRC_CS4(2), 0x00000000},
+ {REG_EASRC_CS4(3), 0x00000000},
+ {REG_EASRC_CS5(0), 0x00000000},
+ {REG_EASRC_CS5(1), 0x00000000},
+ {REG_EASRC_CS5(2), 0x00000000},
+ {REG_EASRC_CS5(3), 0x00000000},
+ {REG_EASRC_DBGC, 0x00000000},
+ {REG_EASRC_DBGS, 0x00000000},
+};
+
+static const struct regmap_range fsl_easrc_readable_ranges[] = {
+ regmap_reg_range(REG_EASRC_RDFIFO(0), REG_EASRC_RCTCH),
+ regmap_reg_range(REG_EASRC_PCF(0), REG_EASRC_PCF(3)),
+ regmap_reg_range(REG_EASRC_CRCC, REG_EASRC_DBGS),
+};
+
+static const struct regmap_access_table fsl_easrc_readable_table = {
+ .yes_ranges = fsl_easrc_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_readable_ranges),
+};
+
+static const struct regmap_range fsl_easrc_writeable_ranges[] = {
+ regmap_reg_range(REG_EASRC_WRFIFO(0), REG_EASRC_WRFIFO(3)),
+ regmap_reg_range(REG_EASRC_CC(0), REG_EASRC_COA(3)),
+ regmap_reg_range(REG_EASRC_RRL(0), REG_EASRC_RCTCH),
+ regmap_reg_range(REG_EASRC_PCF(0), REG_EASRC_DBGC),
+};
+
+static const struct regmap_access_table fsl_easrc_writeable_table = {
+ .yes_ranges = fsl_easrc_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_writeable_ranges),
+};
+
+static const struct regmap_range fsl_easrc_volatileable_ranges[] = {
+ regmap_reg_range(REG_EASRC_RDFIFO(0), REG_EASRC_RDFIFO(3)),
+ regmap_reg_range(REG_EASRC_SFS(0), REG_EASRC_SFS(3)),
+ regmap_reg_range(REG_EASRC_IRQF, REG_EASRC_IRQF),
+ regmap_reg_range(REG_EASRC_DBGS, REG_EASRC_DBGS),
+};
+
+static const struct regmap_access_table fsl_easrc_volatileable_table = {
+ .yes_ranges = fsl_easrc_volatileable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(fsl_easrc_volatileable_ranges),
+};
+
+static const struct regmap_config fsl_easrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_EASRC_DBGS,
+ .reg_defaults = fsl_easrc_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_easrc_reg_defaults),
+ .rd_table = &fsl_easrc_readable_table,
+ .wr_table = &fsl_easrc_writeable_table,
+ .volatile_table = &fsl_easrc_volatileable_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef DEBUG
+static void fsl_easrc_dump_firmware(struct fsl_asrc *easrc)
+{
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct asrc_firmware_hdr *firm = easrc_priv->firmware_hdr;
+ struct interp_params *interp = easrc_priv->interp;
+ struct prefil_params *prefil = easrc_priv->prefil;
+ struct device *dev = &easrc->pdev->dev;
+ int i;
+
+ if (firm->magic != FIRMWARE_MAGIC) {
+ dev_err(dev, "Wrong magic. Something went wrong!");
+ return;
+ }
+
+ dev_dbg(dev, "Firmware v%u dump:\n", firm->firmware_version);
+ dev_dbg(dev, "Num prefitler scenarios: %u\n", firm->prefil_scen);
+ dev_dbg(dev, "Num interpolation scenarios: %u\n", firm->interp_scen);
+ dev_dbg(dev, "\nInterpolation scenarios:\n");
+
+ for (i = 0; i < firm->interp_scen; i++) {
+ if (interp[i].magic != FIRMWARE_MAGIC) {
+ dev_dbg(dev, "%d. wrong interp magic: %x\n",
+ i, interp[i].magic);
+ continue;
+ }
+ dev_dbg(dev, "%d. taps: %u, phases: %u, center: %llu\n", i,
+ interp[i].num_taps, interp[i].num_phases,
+ interp[i].center_tap);
+ }
+
+ for (i = 0; i < firm->prefil_scen; i++) {
+ if (prefil[i].magic != FIRMWARE_MAGIC) {
+ dev_dbg(dev, "%d. wrong prefil magic: %x\n",
+ i, prefil[i].magic);
+ continue;
+ }
+ dev_dbg(dev, "%d. insr: %u, outsr: %u, st1: %u, st2: %u\n", i,
+ prefil[i].insr, prefil[i].outsr,
+ prefil[i].st1_taps, prefil[i].st2_taps);
+ }
+
+ dev_dbg(dev, "end of firmware dump\n");
+}
+#endif
+
+static int fsl_easrc_get_firmware(struct fsl_asrc *easrc)
+{
+ struct fsl_easrc_priv *easrc_priv;
+ const struct firmware **fw_p;
+ u32 pnum, inum, offset;
+ const u8 *data;
+ int ret;
+
+ if (!easrc)
+ return -EINVAL;
+
+ easrc_priv = easrc->private;
+ fw_p = &easrc_priv->fw;
+
+ ret = request_firmware(fw_p, easrc_priv->fw_name, &easrc->pdev->dev);
+ if (ret)
+ return ret;
+
+ data = easrc_priv->fw->data;
+
+ easrc_priv->firmware_hdr = (struct asrc_firmware_hdr *)data;
+ pnum = easrc_priv->firmware_hdr->prefil_scen;
+ inum = easrc_priv->firmware_hdr->interp_scen;
+
+ if (inum) {
+ offset = sizeof(struct asrc_firmware_hdr);
+ easrc_priv->interp = (struct interp_params *)(data + offset);
+ }
+
+ if (pnum) {
+ offset = sizeof(struct asrc_firmware_hdr) +
+ inum * sizeof(struct interp_params);
+ easrc_priv->prefil = (struct prefil_params *)(data + offset);
+ }
+
+#ifdef DEBUG
+ fsl_easrc_dump_firmware(easrc);
+#endif
+
+ return 0;
+}
+
+static irqreturn_t fsl_easrc_isr(int irq, void *dev_id)
+{
+ struct fsl_asrc *easrc = (struct fsl_asrc *)dev_id;
+ struct device *dev = &easrc->pdev->dev;
+ int val;
+
+ regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+
+ if (val & EASRC_IRQF_OER_MASK)
+ dev_dbg(dev, "output FIFO underflow\n");
+
+ if (val & EASRC_IRQF_IFO_MASK)
+ dev_dbg(dev, "input FIFO overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
+{
+ return REG_EASRC_FIFO(dir, index);
+}
+
+static const struct of_device_id fsl_easrc_dt_ids[] = {
+ { .compatible = "fsl,imx8mn-easrc",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_easrc_dt_ids);
+
+static int fsl_easrc_probe(struct platform_device *pdev)
+{
+ struct fsl_easrc_priv *easrc_priv;
+ struct device *dev = &pdev->dev;
+ struct fsl_asrc *easrc;
+ struct resource *res;
+ struct device_node *np;
+ void __iomem *regs;
+ int ret, irq;
+
+ easrc = devm_kzalloc(dev, sizeof(*easrc), GFP_KERNEL);
+ if (!easrc)
+ return -ENOMEM;
+
+ easrc_priv = devm_kzalloc(dev, sizeof(*easrc_priv), GFP_KERNEL);
+ if (!easrc_priv)
+ return -ENOMEM;
+
+ easrc->pdev = pdev;
+ easrc->private = easrc_priv;
+ np = dev->of_node;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(&pdev->dev, "failed ioremap\n");
+ return PTR_ERR(regs);
+ }
+
+ easrc->paddr = res->start;
+
+ easrc->regmap = devm_regmap_init_mmio_clk(dev, "mem", regs,
+ &fsl_easrc_regmap_config);
+ if (IS_ERR(easrc->regmap)) {
+ dev_err(dev, "failed to init regmap");
+ return PTR_ERR(easrc->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no irq for node %pOF\n", np);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, fsl_easrc_isr, 0,
+ dev_name(dev), easrc);
+ if (ret) {
+ dev_err(dev, "failed to claim irq %u: %d\n", irq, ret);
+ return ret;
+ }
+
+ easrc->mem_clk = devm_clk_get(dev, "mem");
+ if (IS_ERR(easrc->mem_clk)) {
+ dev_err(dev, "failed to get mem clock\n");
+ return PTR_ERR(easrc->mem_clk);
+ }
+
+ /* Set default value */
+ easrc->channel_avail = 32;
+ easrc->get_dma_channel = fsl_easrc_get_dma_channel;
+ easrc->request_pair = fsl_easrc_request_context;
+ easrc->release_pair = fsl_easrc_release_context;
+ easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
+
+ easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
+ easrc_priv->const_coeff = 0x3FF0000000000000;
+
+ ret = of_property_read_u32(np, "fsl,asrc-rate", &easrc->asrc_rate);
+ if (ret) {
+ dev_err(dev, "failed to asrc rate\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "fsl,asrc-format", &easrc->asrc_format);
+ if (ret) {
+ dev_err(dev, "failed to asrc format\n");
+ return ret;
+ }
+
+ if (!(FSL_EASRC_FORMATS & (1ULL << easrc->asrc_format))) {
+ dev_warn(dev, "unsupported format, switching to S24_LE\n");
+ easrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ }
+
+ ret = of_property_read_string(np, "firmware-name",
+ &easrc_priv->fw_name);
+ if (ret) {
+ dev_err(dev, "failed to get firmware name\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, easrc);
+ pm_runtime_enable(dev);
+
+ spin_lock_init(&easrc->lock);
+
+ regcache_cache_only(easrc->regmap, true);
+
+ ret = devm_snd_soc_register_component(dev, &fsl_easrc_component,
+ &fsl_easrc_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register ASoC DAI\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &fsl_asrc_component,
+ NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC platform\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_easrc_runtime_suspend(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ unsigned long lock_flags;
+
+ regcache_cache_only(easrc->regmap, true);
+
+ clk_disable_unprepare(easrc->mem_clk);
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ easrc_priv->firmware_loaded = 0;
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ return 0;
+}
+
+static int fsl_easrc_runtime_resume(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv;
+ struct fsl_asrc_pair *ctx;
+ unsigned long lock_flags;
+ int ret;
+ int i;
+
+ ret = clk_prepare_enable(easrc->mem_clk);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(easrc->regmap, false);
+ regcache_mark_dirty(easrc->regmap);
+ regcache_sync(easrc->regmap);
+
+ spin_lock_irqsave(&easrc->lock, lock_flags);
+ if (easrc_priv->firmware_loaded) {
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+ goto skip_load;
+ }
+ easrc_priv->firmware_loaded = 1;
+ spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+ ret = fsl_easrc_get_firmware(easrc);
+ if (ret) {
+ dev_err(dev, "failed to get firmware\n");
+ goto disable_mem_clk;
+ }
+
+ /*
+ * Write Resampling Coefficients
+ * The coefficient RAM must be configured prior to beginning of
+ * any context processing within the ASRC
+ */
+ ret = fsl_easrc_resampler_config(easrc);
+ if (ret) {
+ dev_err(dev, "resampler config failed\n");
+ goto disable_mem_clk;
+ }
+
+ for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+ ctx = easrc->pair[i];
+ if (!ctx)
+ continue;
+
+ ctx_priv = ctx->private;
+ fsl_easrc_set_rs_ratio(ctx);
+ ctx_priv->out_missed_sample = ctx_priv->in_filled_sample *
+ ctx_priv->out_params.sample_rate /
+ ctx_priv->in_params.sample_rate;
+ if (ctx_priv->in_filled_sample * ctx_priv->out_params.sample_rate
+ % ctx_priv->in_params.sample_rate != 0)
+ ctx_priv->out_missed_sample += 1;
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, i,
+ ctx_priv->st1_coeff,
+ ctx_priv->st1_num_taps,
+ ctx_priv->st1_addexp);
+ if (ret)
+ goto disable_mem_clk;
+
+ ret = fsl_easrc_write_pf_coeff_mem(easrc, i,
+ ctx_priv->st2_coeff,
+ ctx_priv->st2_num_taps,
+ ctx_priv->st2_addexp);
+ if (ret)
+ goto disable_mem_clk;
+ }
+
+skip_load:
+ return 0;
+
+disable_mem_clk:
+ clk_disable_unprepare(easrc->mem_clk);
+ return ret;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops fsl_easrc_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_easrc_runtime_suspend,
+ fsl_easrc_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_easrc_driver = {
+ .probe = fsl_easrc_probe,
+ .remove = fsl_easrc_remove,
+ .driver = {
+ .name = "fsl-easrc",
+ .pm = &fsl_easrc_pm_ops,
+ .of_match_table = fsl_easrc_dt_ids,
+ },
+};
+module_platform_driver(fsl_easrc_driver);
+
+MODULE_DESCRIPTION("NXP Enhanced Asynchronous Sample Rate (eASRC) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h
new file mode 100644
index 000000000000..30620d56252c
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.h
@@ -0,0 +1,651 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 NXP
+ */
+
+#ifndef _FSL_EASRC_H
+#define _FSL_EASRC_H
+
+#include <sound/asound.h>
+#include <linux/platform_data/dma-imx.h>
+
+#include "fsl_asrc_common.h"
+
+/* EASRC Register Map */
+
+/* ASRC Input Write FIFO */
+#define REG_EASRC_WRFIFO(ctx) (0x000 + 4 * (ctx))
+/* ASRC Output Read FIFO */
+#define REG_EASRC_RDFIFO(ctx) (0x010 + 4 * (ctx))
+/* ASRC Context Control */
+#define REG_EASRC_CC(ctx) (0x020 + 4 * (ctx))
+/* ASRC Context Control Extended 1 */
+#define REG_EASRC_CCE1(ctx) (0x030 + 4 * (ctx))
+/* ASRC Context Control Extended 2 */
+#define REG_EASRC_CCE2(ctx) (0x040 + 4 * (ctx))
+/* ASRC Control Input Access */
+#define REG_EASRC_CIA(ctx) (0x050 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot0 */
+#define REG_EASRC_DPCS0R0(ctx) (0x060 + 4 * (ctx))
+#define REG_EASRC_DPCS0R1(ctx) (0x070 + 4 * (ctx))
+#define REG_EASRC_DPCS0R2(ctx) (0x080 + 4 * (ctx))
+#define REG_EASRC_DPCS0R3(ctx) (0x090 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot1 */
+#define REG_EASRC_DPCS1R0(ctx) (0x0A0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R1(ctx) (0x0B0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R2(ctx) (0x0C0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R3(ctx) (0x0D0 + 4 * (ctx))
+/* ASRC Context Output Control */
+#define REG_EASRC_COC(ctx) (0x0E0 + 4 * (ctx))
+/* ASRC Control Output Access */
+#define REG_EASRC_COA(ctx) (0x0F0 + 4 * (ctx))
+/* ASRC Sample FIFO Status */
+#define REG_EASRC_SFS(ctx) (0x100 + 4 * (ctx))
+/* ASRC Resampling Ratio Low */
+#define REG_EASRC_RRL(ctx) (0x110 + 8 * (ctx))
+/* ASRC Resampling Ratio High */
+#define REG_EASRC_RRH(ctx) (0x114 + 8 * (ctx))
+/* ASRC Resampling Ratio Update Control */
+#define REG_EASRC_RUC(ctx) (0x130 + 4 * (ctx))
+/* ASRC Resampling Ratio Update Rate */
+#define REG_EASRC_RUR(ctx) (0x140 + 4 * (ctx))
+/* ASRC Resampling Center Tap Coefficient Low */
+#define REG_EASRC_RCTCL (0x150)
+/* ASRC Resampling Center Tap Coefficient High */
+#define REG_EASRC_RCTCH (0x154)
+/* ASRC Prefilter Coefficient FIFO */
+#define REG_EASRC_PCF(ctx) (0x160 + 4 * (ctx))
+/* ASRC Context Resampling Coefficient Memory */
+#define REG_EASRC_CRCM 0x170
+/* ASRC Context Resampling Coefficient Control*/
+#define REG_EASRC_CRCC 0x174
+/* ASRC Interrupt Control */
+#define REG_EASRC_IRQC 0x178
+/* ASRC Interrupt Status Flags */
+#define REG_EASRC_IRQF 0x17C
+/* ASRC Channel Status 0 */
+#define REG_EASRC_CS0(ctx) (0x180 + 4 * (ctx))
+/* ASRC Channel Status 1 */
+#define REG_EASRC_CS1(ctx) (0x190 + 4 * (ctx))
+/* ASRC Channel Status 2 */
+#define REG_EASRC_CS2(ctx) (0x1A0 + 4 * (ctx))
+/* ASRC Channel Status 3 */
+#define REG_EASRC_CS3(ctx) (0x1B0 + 4 * (ctx))
+/* ASRC Channel Status 4 */
+#define REG_EASRC_CS4(ctx) (0x1C0 + 4 * (ctx))
+/* ASRC Channel Status 5 */
+#define REG_EASRC_CS5(ctx) (0x1D0 + 4 * (ctx))
+/* ASRC Debug Control Register */
+#define REG_EASRC_DBGC 0x1E0
+/* ASRC Debug Status Register */
+#define REG_EASRC_DBGS 0x1E4
+
+#define REG_EASRC_FIFO(x, ctx) (x == IN ? REG_EASRC_WRFIFO(ctx) \
+ : REG_EASRC_RDFIFO(ctx))
+
+/* ASRC Context Control (CC) */
+#define EASRC_CC_EN_SHIFT 31
+#define EASRC_CC_EN_MASK BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_EN BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_STOP_SHIFT 29
+#define EASRC_CC_STOP_MASK BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_STOP BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_FWMDE_SHIFT 28
+#define EASRC_CC_FWMDE_MASK BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FWMDE BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FIFO_WTMK_SHIFT 16
+#define EASRC_CC_FIFO_WTMK_WIDTH 7
+#define EASRC_CC_FIFO_WTMK_MASK ((BIT(EASRC_CC_FIFO_WTMK_WIDTH) - 1) \
+ << EASRC_CC_FIFO_WTMK_SHIFT)
+#define EASRC_CC_FIFO_WTMK(v) (((v) << EASRC_CC_FIFO_WTMK_SHIFT) \
+ & EASRC_CC_FIFO_WTMK_MASK)
+#define EASRC_CC_SAMPLE_POS_SHIFT 11
+#define EASRC_CC_SAMPLE_POS_WIDTH 5
+#define EASRC_CC_SAMPLE_POS_MASK ((BIT(EASRC_CC_SAMPLE_POS_WIDTH) - 1) \
+ << EASRC_CC_SAMPLE_POS_SHIFT)
+#define EASRC_CC_SAMPLE_POS(v) (((v) << EASRC_CC_SAMPLE_POS_SHIFT) \
+ & EASRC_CC_SAMPLE_POS_MASK)
+#define EASRC_CC_ENDIANNESS_SHIFT 10
+#define EASRC_CC_ENDIANNESS_MASK BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_ENDIANNESS BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_BPS_SHIFT 8
+#define EASRC_CC_BPS_WIDTH 2
+#define EASRC_CC_BPS_MASK ((BIT(EASRC_CC_BPS_WIDTH) - 1) \
+ << EASRC_CC_BPS_SHIFT)
+#define EASRC_CC_BPS(v) (((v) << EASRC_CC_BPS_SHIFT) \
+ & EASRC_CC_BPS_MASK)
+#define EASRC_CC_FMT_SHIFT 7
+#define EASRC_CC_FMT_MASK BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_FMT BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_INSIGN_SHIFT 6
+#define EASRC_CC_INSIGN_MASK BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_INSIGN BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_CHEN_SHIFT 0
+#define EASRC_CC_CHEN_WIDTH 5
+#define EASRC_CC_CHEN_MASK ((BIT(EASRC_CC_CHEN_WIDTH) - 1) \
+ << EASRC_CC_CHEN_SHIFT)
+#define EASRC_CC_CHEN(v) (((v) << EASRC_CC_CHEN_SHIFT) \
+ & EASRC_CC_CHEN_MASK)
+
+/* ASRC Context Control Extended 1 (CCE1) */
+#define EASRC_CCE1_COEF_WS_SHIFT 25
+#define EASRC_CCE1_COEF_WS_MASK BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_WS BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST_SHIFT 24
+#define EASRC_CCE1_COEF_MEM_RST_MASK BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_PF_EXP_SHIFT 16
+#define EASRC_CCE1_PF_EXP_WIDTH 8
+#define EASRC_CCE1_PF_EXP_MASK ((BIT(EASRC_CCE1_PF_EXP_WIDTH) - 1) \
+ << EASRC_CCE1_PF_EXP_SHIFT)
+#define EASRC_CCE1_PF_EXP(v) (((v) << EASRC_CCE1_PF_EXP_SHIFT) \
+ & EASRC_CCE1_PF_EXP_MASK)
+#define EASRC_CCE1_PF_ST1_WBFP_SHIFT 9
+#define EASRC_CCE1_PF_ST1_WBFP_MASK BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_ST1_WBFP BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_TSEN_SHIFT 8
+#define EASRC_CCE1_PF_TSEN_MASK BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_PF_TSEN BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_RS_BYPASS_SHIFT 7
+#define EASRC_CCE1_RS_BYPASS_MASK BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_BYPASS BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS_SHIFT 6
+#define EASRC_CCE1_PF_BYPASS_MASK BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_STOP_SHIFT 5
+#define EASRC_CCE1_RS_STOP_MASK BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_RS_STOP BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP_SHIFT 4
+#define EASRC_CCE1_PF_STOP_MASK BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_RS_INIT_SHIFT 2
+#define EASRC_CCE1_RS_INIT_WIDTH 2
+#define EASRC_CCE1_RS_INIT_MASK ((BIT(EASRC_CCE1_RS_INIT_WIDTH) - 1) \
+ << EASRC_CCE1_RS_INIT_SHIFT)
+#define EASRC_CCE1_RS_INIT(v) (((v) << EASRC_CCE1_RS_INIT_SHIFT) \
+ & EASRC_CCE1_RS_INIT_MASK)
+#define EASRC_CCE1_PF_INIT_SHIFT 0
+#define EASRC_CCE1_PF_INIT_WIDTH 2
+#define EASRC_CCE1_PF_INIT_MASK ((BIT(EASRC_CCE1_PF_INIT_WIDTH) - 1) \
+ << EASRC_CCE1_PF_INIT_SHIFT)
+#define EASRC_CCE1_PF_INIT(v) (((v) << EASRC_CCE1_PF_INIT_SHIFT) \
+ & EASRC_CCE1_PF_INIT_MASK)
+
+/* ASRC Context Control Extended 2 (CCE2) */
+#define EASRC_CCE2_ST2_TAPS_SHIFT 16
+#define EASRC_CCE2_ST2_TAPS_WIDTH 9
+#define EASRC_CCE2_ST2_TAPS_MASK ((BIT(EASRC_CCE2_ST2_TAPS_WIDTH) - 1) \
+ << EASRC_CCE2_ST2_TAPS_SHIFT)
+#define EASRC_CCE2_ST2_TAPS(v) (((v) << EASRC_CCE2_ST2_TAPS_SHIFT) \
+ & EASRC_CCE2_ST2_TAPS_MASK)
+#define EASRC_CCE2_ST1_TAPS_SHIFT 0
+#define EASRC_CCE2_ST1_TAPS_WIDTH 9
+#define EASRC_CCE2_ST1_TAPS_MASK ((BIT(EASRC_CCE2_ST1_TAPS_WIDTH) - 1) \
+ << EASRC_CCE2_ST1_TAPS_SHIFT)
+#define EASRC_CCE2_ST1_TAPS(v) (((v) << EASRC_CCE2_ST1_TAPS_SHIFT) \
+ & EASRC_CCE2_ST1_TAPS_MASK)
+
+/* ASRC Control Input Access (CIA) */
+#define EASRC_CIA_ITER_SHIFT 16
+#define EASRC_CIA_ITER_WIDTH 6
+#define EASRC_CIA_ITER_MASK ((BIT(EASRC_CIA_ITER_WIDTH) - 1) \
+ << EASRC_CIA_ITER_SHIFT)
+#define EASRC_CIA_ITER(v) (((v) << EASRC_CIA_ITER_SHIFT) \
+ & EASRC_CIA_ITER_MASK)
+#define EASRC_CIA_GRLEN_SHIFT 8
+#define EASRC_CIA_GRLEN_WIDTH 6
+#define EASRC_CIA_GRLEN_MASK ((BIT(EASRC_CIA_GRLEN_WIDTH) - 1) \
+ << EASRC_CIA_GRLEN_SHIFT)
+#define EASRC_CIA_GRLEN(v) (((v) << EASRC_CIA_GRLEN_SHIFT) \
+ & EASRC_CIA_GRLEN_MASK)
+#define EASRC_CIA_ACCLEN_SHIFT 0
+#define EASRC_CIA_ACCLEN_WIDTH 6
+#define EASRC_CIA_ACCLEN_MASK ((BIT(EASRC_CIA_ACCLEN_WIDTH) - 1) \
+ << EASRC_CIA_ACCLEN_SHIFT)
+#define EASRC_CIA_ACCLEN(v) (((v) << EASRC_CIA_ACCLEN_SHIFT) \
+ & EASRC_CIA_ACCLEN_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register0 (DPCS0R0) */
+#define EASRC_DPCS0R0_MAXCH_SHIFT 24
+#define EASRC_DPCS0R0_MAXCH_WIDTH 5
+#define EASRC_DPCS0R0_MAXCH_MASK ((BIT(EASRC_DPCS0R0_MAXCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_MAXCH_SHIFT)
+#define EASRC_DPCS0R0_MAXCH(v) (((v) << EASRC_DPCS0R0_MAXCH_SHIFT) \
+ & EASRC_DPCS0R0_MAXCH_MASK)
+#define EASRC_DPCS0R0_MINCH_SHIFT 16
+#define EASRC_DPCS0R0_MINCH_WIDTH 5
+#define EASRC_DPCS0R0_MINCH_MASK ((BIT(EASRC_DPCS0R0_MINCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_MINCH_SHIFT)
+#define EASRC_DPCS0R0_MINCH(v) (((v) << EASRC_DPCS0R0_MINCH_SHIFT) \
+ & EASRC_DPCS0R0_MINCH_MASK)
+#define EASRC_DPCS0R0_NUMCH_SHIFT 8
+#define EASRC_DPCS0R0_NUMCH_WIDTH 5
+#define EASRC_DPCS0R0_NUMCH_MASK ((BIT(EASRC_DPCS0R0_NUMCH_WIDTH) - 1) \
+ << EASRC_DPCS0R0_NUMCH_SHIFT)
+#define EASRC_DPCS0R0_NUMCH(v) (((v) << EASRC_DPCS0R0_NUMCH_SHIFT) \
+ & EASRC_DPCS0R0_NUMCH_MASK)
+#define EASRC_DPCS0R0_CTXNUM_SHIFT 1
+#define EASRC_DPCS0R0_CTXNUM_WIDTH 2
+#define EASRC_DPCS0R0_CTXNUM_MASK ((BIT(EASRC_DPCS0R0_CTXNUM_WIDTH) - 1) \
+ << EASRC_DPCS0R0_CTXNUM_SHIFT)
+#define EASRC_DPCS0R0_CTXNUM(v) (((v) << EASRC_DPCS0R0_CTXNUM_SHIFT) \
+ & EASRC_DPCS0R0_CTXNUM_MASK)
+#define EASRC_DPCS0R0_EN_SHIFT 0
+#define EASRC_DPCS0R0_EN_MASK BIT(EASRC_DPCS0R0_EN_SHIFT)
+#define EASRC_DPCS0R0_EN BIT(EASRC_DPCS0R0_EN_SHIFT)
+
+/* ASRC Datapath Processor Control Slot0 Register1 (DPCS0R1) */
+#define EASRC_DPCS0R1_ST1_EXP_SHIFT 0
+#define EASRC_DPCS0R1_ST1_EXP_WIDTH 13
+#define EASRC_DPCS0R1_ST1_EXP_MASK ((BIT(EASRC_DPCS0R1_ST1_EXP_WIDTH) - 1) \
+ << EASRC_DPCS0R1_ST1_EXP_SHIFT)
+#define EASRC_DPCS0R1_ST1_EXP(v) (((v) << EASRC_DPCS0R1_ST1_EXP_SHIFT) \
+ & EASRC_DPCS0R1_ST1_EXP_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register2 (DPCS0R2) */
+#define EASRC_DPCS0R2_ST1_MA_SHIFT 16
+#define EASRC_DPCS0R2_ST1_MA_WIDTH 13
+#define EASRC_DPCS0R2_ST1_MA_MASK ((BIT(EASRC_DPCS0R2_ST1_MA_WIDTH) - 1) \
+ << EASRC_DPCS0R2_ST1_MA_SHIFT)
+#define EASRC_DPCS0R2_ST1_MA(v) (((v) << EASRC_DPCS0R2_ST1_MA_SHIFT) \
+ & EASRC_DPCS0R2_ST1_MA_MASK)
+#define EASRC_DPCS0R2_ST1_SA_SHIFT 0
+#define EASRC_DPCS0R2_ST1_SA_WIDTH 13
+#define EASRC_DPCS0R2_ST1_SA_MASK ((BIT(EASRC_DPCS0R2_ST1_SA_WIDTH) - 1) \
+ << EASRC_DPCS0R2_ST1_SA_SHIFT)
+#define EASRC_DPCS0R2_ST1_SA(v) (((v) << EASRC_DPCS0R2_ST1_SA_SHIFT) \
+ & EASRC_DPCS0R2_ST1_SA_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register3 (DPCS0R3) */
+#define EASRC_DPCS0R3_ST2_MA_SHIFT 16
+#define EASRC_DPCS0R3_ST2_MA_WIDTH 13
+#define EASRC_DPCS0R3_ST2_MA_MASK ((BIT(EASRC_DPCS0R3_ST2_MA_WIDTH) - 1) \
+ << EASRC_DPCS0R3_ST2_MA_SHIFT)
+#define EASRC_DPCS0R3_ST2_MA(v) (((v) << EASRC_DPCS0R3_ST2_MA_SHIFT) \
+ & EASRC_DPCS0R3_ST2_MA_MASK)
+#define EASRC_DPCS0R3_ST2_SA_SHIFT 0
+#define EASRC_DPCS0R3_ST2_SA_WIDTH 13
+#define EASRC_DPCS0R3_ST2_SA_MASK ((BIT(EASRC_DPCS0R3_ST2_SA_WIDTH) - 1) \
+ << EASRC_DPCS0R3_ST2_SA_SHIFT)
+#define EASRC_DPCS0R3_ST2_SA(v) (((v) << EASRC_DPCS0R3_ST2_SA_SHIFT) \
+ & EASRC_DPCS0R3_ST2_SA_MASK)
+
+/* ASRC Context Output Control (COC) */
+#define EASRC_COC_FWMDE_SHIFT 28
+#define EASRC_COC_FWMDE_MASK BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FWMDE BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FIFO_WTMK_SHIFT 16
+#define EASRC_COC_FIFO_WTMK_WIDTH 7
+#define EASRC_COC_FIFO_WTMK_MASK ((BIT(EASRC_COC_FIFO_WTMK_WIDTH) - 1) \
+ << EASRC_COC_FIFO_WTMK_SHIFT)
+#define EASRC_COC_FIFO_WTMK(v) (((v) << EASRC_COC_FIFO_WTMK_SHIFT) \
+ & EASRC_COC_FIFO_WTMK_MASK)
+#define EASRC_COC_SAMPLE_POS_SHIFT 11
+#define EASRC_COC_SAMPLE_POS_WIDTH 5
+#define EASRC_COC_SAMPLE_POS_MASK ((BIT(EASRC_COC_SAMPLE_POS_WIDTH) - 1) \
+ << EASRC_COC_SAMPLE_POS_SHIFT)
+#define EASRC_COC_SAMPLE_POS(v) (((v) << EASRC_COC_SAMPLE_POS_SHIFT) \
+ & EASRC_COC_SAMPLE_POS_MASK)
+#define EASRC_COC_ENDIANNESS_SHIFT 10
+#define EASRC_COC_ENDIANNESS_MASK BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_ENDIANNESS BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_BPS_SHIFT 8
+#define EASRC_COC_BPS_WIDTH 2
+#define EASRC_COC_BPS_MASK ((BIT(EASRC_COC_BPS_WIDTH) - 1) \
+ << EASRC_COC_BPS_SHIFT)
+#define EASRC_COC_BPS(v) (((v) << EASRC_COC_BPS_SHIFT) \
+ & EASRC_COC_BPS_MASK)
+#define EASRC_COC_FMT_SHIFT 7
+#define EASRC_COC_FMT_MASK BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_FMT BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_OUTSIGN_SHIFT 6
+#define EASRC_COC_OUTSIGN_MASK BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_OUTSIGN_OUT BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_IEC_VDATA_SHIFT 2
+#define EASRC_COC_IEC_VDATA_MASK BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_VDATA BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_EN_SHIFT 1
+#define EASRC_COC_IEC_EN_MASK BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_IEC_EN BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_DITHER_EN_SHIFT 0
+#define EASRC_COC_DITHER_EN_MASK BIT(EASRC_COC_DITHER_EN_SHIFT)
+#define EASRC_COC_DITHER_EN BIT(EASRC_COC_DITHER_EN_SHIFT)
+
+/* ASRC Control Output Access (COA) */
+#define EASRC_COA_ITER_SHIFT 16
+#define EASRC_COA_ITER_WIDTH 6
+#define EASRC_COA_ITER_MASK ((BIT(EASRC_COA_ITER_WIDTH) - 1) \
+ << EASRC_COA_ITER_SHIFT)
+#define EASRC_COA_ITER(v) (((v) << EASRC_COA_ITER_SHIFT) \
+ & EASRC_COA_ITER_MASK)
+#define EASRC_COA_GRLEN_SHIFT 8
+#define EASRC_COA_GRLEN_WIDTH 6
+#define EASRC_COA_GRLEN_MASK ((BIT(EASRC_COA_GRLEN_WIDTH) - 1) \
+ << EASRC_COA_GRLEN_SHIFT)
+#define EASRC_COA_GRLEN(v) (((v) << EASRC_COA_GRLEN_SHIFT) \
+ & EASRC_COA_GRLEN_MASK)
+#define EASRC_COA_ACCLEN_SHIFT 0
+#define EASRC_COA_ACCLEN_WIDTH 6
+#define EASRC_COA_ACCLEN_MASK ((BIT(EASRC_COA_ACCLEN_WIDTH) - 1) \
+ << EASRC_COA_ACCLEN_SHIFT)
+#define EASRC_COA_ACCLEN(v) (((v) << EASRC_COA_ACCLEN_SHIFT) \
+ & EASRC_COA_ACCLEN_MASK)
+
+/* ASRC Sample FIFO Status (SFS) */
+#define EASRC_SFS_IWTMK_SHIFT 23
+#define EASRC_SFS_IWTMK_MASK BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_IWTMK BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_NSGI_SHIFT 16
+#define EASRC_SFS_NSGI_WIDTH 7
+#define EASRC_SFS_NSGI_MASK ((BIT(EASRC_SFS_NSGI_WIDTH) - 1) \
+ << EASRC_SFS_NSGI_SHIFT)
+#define EASRC_SFS_NSGI(v) (((v) << EASRC_SFS_NSGI_SHIFT) \
+ & EASRC_SFS_NSGI_MASK)
+#define EASRC_SFS_OWTMK_SHIFT 7
+#define EASRC_SFS_OWTMK_MASK BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_OWTMK BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_NSGO_SHIFT 0
+#define EASRC_SFS_NSGO_WIDTH 7
+#define EASRC_SFS_NSGO_MASK ((BIT(EASRC_SFS_NSGO_WIDTH) - 1) \
+ << EASRC_SFS_NSGO_SHIFT)
+#define EASRC_SFS_NSGO(v) (((v) << EASRC_SFS_NSGO_SHIFT) \
+ & EASRC_SFS_NSGO_MASK)
+
+/* ASRC Resampling Ratio Low (RRL) */
+#define EASRC_RRL_RS_RL_SHIFT 0
+#define EASRC_RRL_RS_RL_WIDTH 32
+#define EASRC_RRL_RS_RL(v) ((v) << EASRC_RRL_RS_RL_SHIFT)
+
+/* ASRC Resampling Ratio High (RRH) */
+#define EASRC_RRH_RS_VLD_SHIFT 31
+#define EASRC_RRH_RS_VLD_MASK BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_VLD BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_RH_SHIFT 0
+#define EASRC_RRH_RS_RH_WIDTH 12
+#define EASRC_RRH_RS_RH_MASK ((BIT(EASRC_RRH_RS_RH_WIDTH) - 1) \
+ << EASRC_RRH_RS_RH_SHIFT)
+#define EASRC_RRH_RS_RH(v) (((v) << EASRC_RRH_RS_RH_SHIFT) \
+ & EASRC_RRH_RS_RH_MASK)
+
+/* ASRC Resampling Ratio Update Control (RSUC) */
+#define EASRC_RSUC_RS_RM_SHIFT 0
+#define EASRC_RSUC_RS_RM_WIDTH 32
+#define EASRC_RSUC_RS_RM(v) ((v) << EASRC_RSUC_RS_RM_SHIFT)
+
+/* ASRC Resampling Ratio Update Rate (RRUR) */
+#define EASRC_RRUR_RRR_SHIFT 0
+#define EASRC_RRUR_RRR_WIDTH 31
+#define EASRC_RRUR_RRR_MASK ((BIT(EASRC_RRUR_RRR_WIDTH) - 1) \
+ << EASRC_RRUR_RRR_SHIFT)
+#define EASRC_RRUR_RRR(v) (((v) << EASRC_RRUR_RRR_SHIFT) \
+ & EASRC_RRUR_RRR_MASK)
+
+/* ASRC Resampling Center Tap Coefficient Low (RCTCL) */
+#define EASRC_RCTCL_RS_CL_SHIFT 0
+#define EASRC_RCTCL_RS_CL_WIDTH 32
+#define EASRC_RCTCL_RS_CL(v) ((v) << EASRC_RCTCL_RS_CL_SHIFT)
+
+/* ASRC Resampling Center Tap Coefficient High (RCTCH) */
+#define EASRC_RCTCH_RS_CH_SHIFT 0
+#define EASRC_RCTCH_RS_CH_WIDTH 32
+#define EASRC_RCTCH_RS_CH(v) ((v) << EASRC_RCTCH_RS_CH_SHIFT)
+
+/* ASRC Prefilter Coefficient FIFO (PCF) */
+#define EASRC_PCF_CD_SHIFT 0
+#define EASRC_PCF_CD_WIDTH 32
+#define EASRC_PCF_CD(v) ((v) << EASRC_PCF_CD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Memory (CRCM) */
+#define EASRC_CRCM_RS_CWD_SHIFT 0
+#define EASRC_CRCM_RS_CWD_WIDTH 32
+#define EASRC_CRCM_RS_CWD(v) ((v) << EASRC_CRCM_RS_CWD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Control (CRCC) */
+#define EASRC_CRCC_RS_CA_SHIFT 16
+#define EASRC_CRCC_RS_CA_WIDTH 11
+#define EASRC_CRCC_RS_CA_MASK ((BIT(EASRC_CRCC_RS_CA_WIDTH) - 1) \
+ << EASRC_CRCC_RS_CA_SHIFT)
+#define EASRC_CRCC_RS_CA(v) (((v) << EASRC_CRCC_RS_CA_SHIFT) \
+ & EASRC_CRCC_RS_CA_MASK)
+#define EASRC_CRCC_RS_TAPS_SHIFT 1
+#define EASRC_CRCC_RS_TAPS_WIDTH 2
+#define EASRC_CRCC_RS_TAPS_MASK ((BIT(EASRC_CRCC_RS_TAPS_WIDTH) - 1) \
+ << EASRC_CRCC_RS_TAPS_SHIFT)
+#define EASRC_CRCC_RS_TAPS(v) (((v) << EASRC_CRCC_RS_TAPS_SHIFT) \
+ & EASRC_CRCC_RS_TAPS_MASK)
+#define EASRC_CRCC_RS_CPR_SHIFT 0
+#define EASRC_CRCC_RS_CPR_MASK BIT(EASRC_CRCC_RS_CPR_SHIFT)
+#define EASRC_CRCC_RS_CPR BIT(EASRC_CRCC_RS_CPR_SHIFT)
+
+/* ASRC Interrupt_Control (IC) */
+#define EASRC_IRQC_RSDM_SHIFT 8
+#define EASRC_IRQC_RSDM_WIDTH 4
+#define EASRC_IRQC_RSDM_MASK ((BIT(EASRC_IRQC_RSDM_WIDTH) - 1) \
+ << EASRC_IRQC_RSDM_SHIFT)
+#define EASRC_IRQC_RSDM(v) (((v) << EASRC_IRQC_RSDM_SHIFT) \
+ & EASRC_IRQC_RSDM_MASK)
+#define EASRC_IRQC_OERM_SHIFT 4
+#define EASRC_IRQC_OERM_WIDTH 4
+#define EASRC_IRQC_OERM_MASK ((BIT(EASRC_IRQC_OERM_WIDTH) - 1) \
+ << EASRC_IRQC_OERM_SHIFT)
+#define EASRC_IRQC_OERM(v) (((v) << EASRC_IRQC_OERM_SHIFT) \
+ & EASRC_IEQC_OERM_MASK)
+#define EASRC_IRQC_IOM_SHIFT 0
+#define EASRC_IRQC_IOM_WIDTH 4
+#define EASRC_IRQC_IOM_MASK ((BIT(EASRC_IRQC_IOM_WIDTH) - 1) \
+ << EASRC_IRQC_IOM_SHIFT)
+#define EASRC_IRQC_IOM(v) (((v) << EASRC_IRQC_IOM_SHIFT) \
+ & EASRC_IRQC_IOM_MASK)
+
+/* ASRC Interrupt Status Flags (ISF) */
+#define EASRC_IRQF_RSD_SHIFT 8
+#define EASRC_IRQF_RSD_WIDTH 4
+#define EASRC_IRQF_RSD_MASK ((BIT(EASRC_IRQF_RSD_WIDTH) - 1) \
+ << EASRC_IRQF_RSD_SHIFT)
+#define EASRC_IRQF_RSD(v) (((v) << EASRC_IRQF_RSD_SHIFT) \
+ & EASRC_IRQF_RSD_MASK)
+#define EASRC_IRQF_OER_SHIFT 4
+#define EASRC_IRQF_OER_WIDTH 4
+#define EASRC_IRQF_OER_MASK ((BIT(EASRC_IRQF_OER_WIDTH) - 1) \
+ << EASRC_IRQF_OER_SHIFT)
+#define EASRC_IRQF_OER(v) (((v) << EASRC_IRQF_OER_SHIFT) \
+ & EASRC_IRQF_OER_MASK)
+#define EASRC_IRQF_IFO_SHIFT 0
+#define EASRC_IRQF_IFO_WIDTH 4
+#define EASRC_IRQF_IFO_MASK ((BIT(EASRC_IRQF_IFO_WIDTH) - 1) \
+ << EASRC_IRQF_IFO_SHIFT)
+#define EASRC_IRQF_IFO(v) (((v) << EASRC_IRQF_IFO_SHIFT) \
+ & EASRC_IRQF_IFO_MASK)
+
+/* ASRC Context Channel STAT */
+#define EASRC_CSx_CSx_SHIFT 0
+#define EASRC_CSx_CSx_WIDTH 32
+#define EASRC_CSx_CSx(v) ((v) << EASRC_CSx_CSx_SHIFT)
+
+/* ASRC Debug Control Register */
+#define EASRC_DBGC_DMS_SHIFT 0
+#define EASRC_DBGC_DMS_WIDTH 6
+#define EASRC_DBGC_DMS_MASK ((BIT(EASRC_DBGC_DMS_WIDTH) - 1) \
+ << EASRC_DBGC_DMS_SHIFT)
+#define EASRC_DBGC_DMS(v) (((v) << EASRC_DBGC_DMS_SHIFT) \
+ & EASRC_DBGC_DMS_MASK)
+
+/* ASRC Debug Status Register */
+#define EASRC_DBGS_DS_SHIFT 0
+#define EASRC_DBGS_DS_WIDTH 32
+#define EASRC_DBGS_DS(v) ((v) << EASRC_DBGS_DS_SHIFT)
+
+/* General Constants */
+#define EASRC_CTX_MAX_NUM 4
+#define EASRC_RS_COEFF_MEM 0
+#define EASRC_PF_COEFF_MEM 1
+
+/* Prefilter constants */
+#define EASRC_PF_ST1_ONLY 0
+#define EASRC_PF_TWO_STAGE_MODE 1
+#define EASRC_PF_ST1_COEFF_WR 0
+#define EASRC_PF_ST2_COEFF_WR 1
+#define EASRC_MAX_PF_TAPS 384
+
+/* Resampling constants */
+#define EASRC_RS_32_TAPS 0
+#define EASRC_RS_64_TAPS 1
+#define EASRC_RS_128_TAPS 2
+
+/* Initialization mode */
+#define EASRC_INIT_MODE_SW_CONTROL 0
+#define EASRC_INIT_MODE_REPLICATE 1
+#define EASRC_INIT_MODE_ZERO_FILL 2
+
+/* FIFO watermarks */
+#define FSL_EASRC_INPUTFIFO_WML 0x4
+#define FSL_EASRC_OUTPUTFIFO_WML 0x1
+
+#define EASRC_INPUTFIFO_THRESHOLD_MIN 0
+#define EASRC_INPUTFIFO_THRESHOLD_MAX 127
+#define EASRC_OUTPUTFIFO_THRESHOLD_MIN 0
+#define EASRC_OUTPUTFIFO_THRESHOLD_MAX 63
+
+#define EASRC_DMA_BUFFER_SIZE (1024 * 48 * 9)
+#define EASRC_MAX_BUFFER_SIZE (1024 * 48)
+
+#define FIRMWARE_MAGIC 0xDEAD
+#define FIRMWARE_VERSION 1
+
+#define PREFILTER_MEM_LEN 0x1800
+
+enum easrc_word_width {
+ EASRC_WIDTH_16_BIT = 0,
+ EASRC_WIDTH_20_BIT = 1,
+ EASRC_WIDTH_24_BIT = 2,
+ EASRC_WIDTH_32_BIT = 3,
+};
+
+struct __attribute__((__packed__)) asrc_firmware_hdr {
+ u32 magic;
+ u32 interp_scen;
+ u32 prefil_scen;
+ u32 firmware_version;
+};
+
+struct __attribute__((__packed__)) interp_params {
+ u32 magic;
+ u32 num_taps;
+ u32 num_phases;
+ u64 center_tap;
+ u64 coeff[8192];
+};
+
+struct __attribute__((__packed__)) prefil_params {
+ u32 magic;
+ u32 insr;
+ u32 outsr;
+ u32 st1_taps;
+ u32 st2_taps;
+ u32 st1_exp;
+ u64 coeff[256];
+};
+
+struct dma_block {
+ void *dma_vaddr;
+ unsigned int length;
+ unsigned int max_buf_size;
+};
+
+struct fsl_easrc_data_fmt {
+ unsigned int width : 2;
+ unsigned int endianness : 1;
+ unsigned int unsign : 1;
+ unsigned int floating_point : 1;
+ unsigned int iec958: 1;
+ unsigned int sample_pos: 5;
+ unsigned int addexp;
+};
+
+struct fsl_easrc_io_params {
+ struct fsl_easrc_data_fmt fmt;
+ unsigned int group_len;
+ unsigned int iterations;
+ unsigned int access_len;
+ unsigned int fifo_wtmk;
+ unsigned int sample_rate;
+ unsigned int sample_format;
+ unsigned int norm_rate;
+};
+
+struct fsl_easrc_slot {
+ bool busy;
+ int ctx_index;
+ int slot_index;
+ int num_channel; /* maximum is 8 */
+ int min_channel;
+ int max_channel;
+ int pf_mem_used;
+};
+
+/**
+ * fsl_easrc_ctx_priv: EASRC context private data
+ *
+ * @in_params: input parameter
+ * @out_params: output parameter
+ * @st1_num_taps: tap number of stage 1
+ * @st2_num_taps: tap number of stage 2
+ * @st1_num_exp: exponent number of stage 1
+ * @pf_init_mode: prefilter init mode
+ * @rs_init_mode: resample filter init mode
+ * @ctx_streams: stream flag of ctx
+ * @rs_ratio: resampler ratio
+ * @st1_coeff: pointer of stage 1 coeff
+ * @st2_coeff: pointer of stage 2 coeff
+ * @in_filled_sample: input filled sample
+ * @out_missed_sample: sample missed in output
+ * @st1_addexp: exponent added for stage1
+ * @st2_addexp: exponent added for stage2
+ */
+struct fsl_easrc_ctx_priv {
+ struct fsl_easrc_io_params in_params;
+ struct fsl_easrc_io_params out_params;
+ unsigned int st1_num_taps;
+ unsigned int st2_num_taps;
+ unsigned int st1_num_exp;
+ unsigned int pf_init_mode;
+ unsigned int rs_init_mode;
+ unsigned int ctx_streams;
+ u64 rs_ratio;
+ u64 *st1_coeff;
+ u64 *st2_coeff;
+ int in_filled_sample;
+ int out_missed_sample;
+ int st1_addexp;
+ int st2_addexp;
+};
+
+/**
+ * fsl_easrc_priv: EASRC private data
+ *
+ * @slot: slot setting
+ * @firmware_hdr: the header of firmware
+ * @interp: pointer to interpolation filter coeff
+ * @prefil: pointer to prefilter coeff
+ * @fw: firmware of coeff table
+ * @fw_name: firmware name
+ * @rs_num_taps: resample filter taps, 32, 64, or 128
+ * @bps_iec958: bits per sample of iec958
+ * @rs_coeff: resampler coefficient
+ * @const_coeff: one tap prefilter coefficient
+ * @firmware_loaded: firmware is loaded
+ */
+struct fsl_easrc_priv {
+ struct fsl_easrc_slot slot[EASRC_CTX_MAX_NUM][2];
+ struct asrc_firmware_hdr *firmware_hdr;
+ struct interp_params *interp;
+ struct prefil_params *prefil;
+ const struct firmware *fw;
+ const char *fw_name;
+ unsigned int rs_num_taps;
+ unsigned int bps_iec958[EASRC_CTX_MAX_NUM];
+ u64 *rs_coeff;
+ u64 const_coeff;
+ int firmware_loaded;
+};
+#endif /* _FSL_EASRC_H */
--
2.21.0
^ permalink raw reply related
* Re: [PATCH v2 06/16] powerpc/watchpoint: Provide DAWR number to __set_breakpoint
From: Ravi Bangoria @ 2020-04-01 8:57 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <89f43001-0fa4-1394-4158-878fca4962e3@c-s.fr>
On 4/1/20 12:33 PM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
>> Introduce new parameter 'nr' to __set_breakpoint() which indicates
>> which DAWR should be programed. Also convert current_brk variable
>> to an array.
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>> arch/powerpc/include/asm/debug.h | 2 +-
>> arch/powerpc/include/asm/hw_breakpoint.h | 2 +-
>> arch/powerpc/kernel/hw_breakpoint.c | 8 ++++----
>> arch/powerpc/kernel/process.c | 14 +++++++-------
>> arch/powerpc/kernel/signal.c | 2 +-
>> arch/powerpc/xmon/xmon.c | 2 +-
>> 6 files changed, 15 insertions(+), 15 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/debug.h b/arch/powerpc/include/asm/debug.h
>> index 7756026b95ca..6228935a8b64 100644
>> --- a/arch/powerpc/include/asm/debug.h
>> +++ b/arch/powerpc/include/asm/debug.h
>> @@ -45,7 +45,7 @@ static inline int debugger_break_match(struct pt_regs *regs) { return 0; }
>> static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
>> #endif
>> -void __set_breakpoint(struct arch_hw_breakpoint *brk);
>> +void __set_breakpoint(struct arch_hw_breakpoint *brk, int nr);
>
> Same, I think it would make more sense to have nr as first argument.
Sorry, didn't get your point. How will that help?
Ravi
^ permalink raw reply
* Re: [PATCH v2 08/16] powerpc/watchpoint: Disable all available watchpoints when !dawr_force_enable
From: Ravi Bangoria @ 2020-04-01 9:00 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <1bef7056-b862-3b20-c3b8-8b161511c60a@c-s.fr>
On 4/1/20 12:03 PM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
>> Instead of disabling only first watchpoint, disable all available
>> watchpoints while clearing dawr_force_enable.
>
> Can you also explain why you change the function name ?
Right. I should have. Will add it in next version.
>
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>> arch/powerpc/kernel/dawr.c | 10 +++++++---
>> 1 file changed, 7 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/powerpc/kernel/dawr.c b/arch/powerpc/kernel/dawr.c
>> index 311e51ee09f4..5c882f07ac7d 100644
>> --- a/arch/powerpc/kernel/dawr.c
>> +++ b/arch/powerpc/kernel/dawr.c
>> @@ -50,9 +50,13 @@ int set_dawr(struct arch_hw_breakpoint *brk, int nr)
>> return 0;
>> }
>> -static void set_dawr_cb(void *info)
>> +static void disable_dawrs(void *info)
>
> Wouldn't it be better to keep _cb at the end of the function ?
Sure.
Ravi
^ permalink raw reply
* Re: [PATCH v6 1/7] ASoC: fsl_asrc: rename asrc_priv to asrc
From: Nicolin Chen @ 2020-04-01 9:00 UTC (permalink / raw)
To: Shengjiu Wang
Cc: mark.rutland, devicetree, alsa-devel, timur, Xiubo.Lee,
linuxppc-dev, tiwai, lgirdwood, robh+dt, perex, broonie, festevam,
linux-kernel
In-Reply-To: <4a808f376c297f91da0caff9d0f73efb6f152e72.1585726761.git.shengjiu.wang@nxp.com>
On Wed, Apr 01, 2020 at 04:45:34PM +0800, Shengjiu Wang wrote:
> In order to move common structure to fsl_asrc_common.h
> we change the name of asrc_priv to asrc, the asrc_priv
> will be used by new struct fsl_asrc_priv.
>
> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Acked-by: Nicolin Chen <nicoleotsuka@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 09/16] powerpc/watchpoint: Convert thread_struct->hw_brk to an array
From: Ravi Bangoria @ 2020-04-01 9:06 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <e5af5ed7-b9df-e334-1bdb-e7f82ae32697@c-s.fr>
>> static void set_debug_reg_defaults(struct thread_struct *thread)
>> {
>> - thread->hw_brk.address = 0;
>> - thread->hw_brk.type = 0;
>> - thread->hw_brk.len = 0;
>> - thread->hw_brk.hw_len = 0;
>> - if (ppc_breakpoint_available())
>> - set_breakpoint(&thread->hw_brk);
>> + int i;
>> +
>> + for (i = 0; i < nr_wp_slots(); i++) {
>
> Maybe you could add the following that you added other places:
>
> struct arch_hw_breakpoint null_brk = {0};
>
> Then do
>
> thread->hw_brk[i] = null_brk;
Yes that's better.
[...]
>> +static void switch_hw_breakpoint(struct task_struct *new)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < nr_wp_slots(); i++) {
>> + if (unlikely(!hw_brk_match(this_cpu_ptr(¤t_brk[i]),
>> + &new->thread.hw_brk[i]))) {
>> + __set_breakpoint(&new->thread.hw_brk[i], i);
>> + }
>
> Or could be:
>
> if (likely(hw_brk_match(this_cpu_ptr(¤t_brk[i]),
> &new->thread.hw_brk[i])))
> continue;
> __set_breakpoint(&new->thread.hw_brk[i], i);
>
Sure.
[...]
>> @@ -128,8 +131,10 @@ static void do_signal(struct task_struct *tsk)
>> * user space. The DABR will have been cleared if it
>> * triggered inside the kernel.
>> */
>> - if (tsk->thread.hw_brk.address && tsk->thread.hw_brk.type)
>> - __set_breakpoint(&tsk->thread.hw_brk, 0);
>> + for (i = 0; i < nr_wp_slots(); i++) {
>> + if (tsk->thread.hw_brk[i].address && tsk->thread.hw_brk[i].type)
>> + __set_breakpoint(&tsk->thread.hw_brk[i], i);
>> + }
>
> thread.hwbrk also exists when CONFIG_PPC_ADV_DEBUG_REGS is selected.
>
> You could replace the #ifndef CONFIG_PPC_ADV_DEBUG_REGS by an if (!IS_ENABLED(CONFIG_PPC_ADV_DEBUG_REGS)) and then no need of an ifdef when declaring the int i;
Makes sense. Will change it.
Ravi
^ permalink raw reply
* Re: [PATCH v2] qtpm2: Export tpm2_get_cc_attrs_tbl for ibmvtpm driver as module
From: Sachin Sant @ 2020-04-01 9:10 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Stefan Berger, LKML, linux-next, linux-integrity, linuxppc-dev,
Stefan Berger
In-Reply-To: <20200319195706.GD24804@linux.intel.com>
> On 20-Mar-2020, at 1:27 AM, Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> wrote:
>
> On Wed, Mar 18, 2020 at 09:00:17PM -0400, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> This patch fixes the following problem when the ibmvtpm driver
>> is built as a module:
>>
>> ERROR: modpost: "tpm2_get_cc_attrs_tbl" [drivers/char/tpm/tpm_ibmvtpm.ko] undefined!
>> make[1]: *** [scripts/Makefile.modpost:94: __modpost] Error 1
>> make: *** [Makefile:1298: modules] Error 2
>>
>> Fixes: 18b3670d79ae ("tpm: ibmvtpm: Add support for TPM2")
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
>> Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
>
Ping. This failure can now be seen in mainline (cad18da0af) as well.
Thanks
-Sachin
^ permalink raw reply
* Re: [PATCH v2 06/16] powerpc/watchpoint: Provide DAWR number to __set_breakpoint
From: Christophe Leroy @ 2020-04-01 9:11 UTC (permalink / raw)
To: Ravi Bangoria
Cc: apopple, mikey, peterz, oleg, npiggin, linux-kernel, paulus,
jolsa, fweisbec, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <cb9ccaa1-9ea2-c3d7-4724-f75ebff4b66d@linux.ibm.com>
Le 01/04/2020 à 10:57, Ravi Bangoria a écrit :
>
>
> On 4/1/20 12:33 PM, Christophe Leroy wrote:
>>
>>
>> Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
>>> Introduce new parameter 'nr' to __set_breakpoint() which indicates
>>> which DAWR should be programed. Also convert current_brk variable
>>> to an array.
>>>
>>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>>> ---
>>> arch/powerpc/include/asm/debug.h | 2 +-
>>> arch/powerpc/include/asm/hw_breakpoint.h | 2 +-
>>> arch/powerpc/kernel/hw_breakpoint.c | 8 ++++----
>>> arch/powerpc/kernel/process.c | 14 +++++++-------
>>> arch/powerpc/kernel/signal.c | 2 +-
>>> arch/powerpc/xmon/xmon.c | 2 +-
>>> 6 files changed, 15 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/arch/powerpc/include/asm/debug.h
>>> b/arch/powerpc/include/asm/debug.h
>>> index 7756026b95ca..6228935a8b64 100644
>>> --- a/arch/powerpc/include/asm/debug.h
>>> +++ b/arch/powerpc/include/asm/debug.h
>>> @@ -45,7 +45,7 @@ static inline int debugger_break_match(struct
>>> pt_regs *regs) { return 0; }
>>> static inline int debugger_fault_handler(struct pt_regs *regs) {
>>> return 0; }
>>> #endif
>>> -void __set_breakpoint(struct arch_hw_breakpoint *brk);
>>> +void __set_breakpoint(struct arch_hw_breakpoint *brk, int nr);
>>
>> Same, I think it would make more sense to have nr as first argument.
>
> Sorry, didn't get your point. How will that help?
>
Well, it is a tiny detail but for me it is more natural to first tel
which element you modify before telling how you modify it.
Christophe
^ permalink raw reply
* Re: [PATCH v2 13/16] powerpc/watchpoint: Prepare handler to handle more than one watcnhpoint
From: Ravi Bangoria @ 2020-04-01 9:13 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <6b89991b-481a-8cbd-b5b7-559e5e16cf92@c-s.fr>
On 4/1/20 12:20 PM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
>> Currently we assume that we have only one watchpoint supported by hw.
>> Get rid of that assumption and use dynamic loop instead. This should
>> make supporting more watchpoints very easy.
>>
>> With more than one watchpoint, exception handler need to know which
>> DAWR caused the exception, and hw currently does not provide it. So
>> we need sw logic for the same. To figure out which DAWR caused the
>> exception, check all different combinations of user specified range,
>> dawr address range, actual access range and dawrx constrains. For ex,
>> if user specified range and actual access range overlaps but dawrx is
>> configured for readonly watchpoint and the instruction is store, this
>> DAWR must not have caused exception.
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>> arch/powerpc/include/asm/processor.h | 2 +-
>> arch/powerpc/include/asm/sstep.h | 2 +
>> arch/powerpc/kernel/hw_breakpoint.c | 396 +++++++++++++++++++++------
>> arch/powerpc/kernel/process.c | 3 -
>> 4 files changed, 313 insertions(+), 90 deletions(-)
>>
>
> [...]
>
>> -static bool
>> -dar_range_overlaps(unsigned long dar, int size, struct arch_hw_breakpoint *info)
>> +static bool dar_user_range_overlaps(unsigned long dar, int size,
>> + struct arch_hw_breakpoint *info)
>> {
>> return ((dar <= info->address + info->len - 1) &&
>> (dar + size - 1 >= info->address));
>> }
>
> Here and several other places, I think it would be more clear if you could avoid the - 1 :
>
> return ((dar < info->address + info->len) &&
> (dar + size > info->address));
Ok. see below...
>
>
>> +static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info)
>> +{
>> + unsigned long hw_start_addr, hw_end_addr;
>> +
>> + hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE);
>> + hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE) - 1;
>> +
>> + return ((hw_start_addr <= dar) && (hw_end_addr >= dar));
>> +}
>
> hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE);
>
> return ((hw_start_addr <= dar) && (hw_end_addr > dar));
I'm using -1 while calculating end address is to make it
inclusive. If I don't use -1, the end address points to a
location outside of actual range, i.e. it's not really an
end address.
Ravi
^ permalink raw reply
* Re: [PATCH v2 07/16] powerpc/watchpoint: Get watchpoint count dynamically while disabling them
From: Ravi Bangoria @ 2020-04-01 9:19 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <3c2312bb-9689-830e-3bc8-c828eddf369c@c-s.fr>
On 4/1/20 12:02 PM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
>> Instead of disabling only one watchpoint, get num of available
>> watchpoints dynamically and disable all of them.
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>> arch/powerpc/include/asm/hw_breakpoint.h | 15 +++++++--------
>> 1 file changed, 7 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
>> index 4e4976c3248b..fae33c729ba9 100644
>> --- a/arch/powerpc/include/asm/hw_breakpoint.h
>> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
>> @@ -78,14 +78,13 @@ extern void ptrace_triggered(struct perf_event *bp,
>> struct perf_sample_data *data, struct pt_regs *regs);
>> static inline void hw_breakpoint_disable(void)
>> {
>> - struct arch_hw_breakpoint brk;
>> -
>> - brk.address = 0;
>> - brk.type = 0;
>> - brk.len = 0;
>> - brk.hw_len = 0;
>> - if (ppc_breakpoint_available())
>> - __set_breakpoint(&brk, 0);
>> + int i;
>> + struct arch_hw_breakpoint null_brk = {0};
>
> Those declaration should be in the block unsigned them below.
>
>> +
>> + if (ppc_breakpoint_available()) {
>> + for (i = 0; i < nr_wp_slots(); i++)
>> + __set_breakpoint(&null_brk, i);
>> + }
>
> I would have had a preference to the following, but that's detail I guess:
>
> int i;
> struct arch_hw_breakpoint null_brk = {0};
>
> if (!ppc_breakpoint_available())
> return;
>
> for (i = 0; i < nr_wp_slots(); i++)
> __set_breakpoint(&null_brk, i);
This looks more better. Will change it.
Ravi
^ permalink raw reply
* Re: [PATCH v2 13/16] powerpc/watchpoint: Prepare handler to handle more than one watcnhpoint
From: Christophe Leroy @ 2020-04-01 9:20 UTC (permalink / raw)
To: Ravi Bangoria
Cc: apopple, mikey, peterz, oleg, npiggin, linux-kernel, paulus,
jolsa, fweisbec, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <cb2c250b-c963-45fe-f3b4-879076c495ab@linux.ibm.com>
Le 01/04/2020 à 11:13, Ravi Bangoria a écrit :
>
>
> On 4/1/20 12:20 PM, Christophe Leroy wrote:
>>
>>
>> Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
>>> Currently we assume that we have only one watchpoint supported by hw.
>>> Get rid of that assumption and use dynamic loop instead. This should
>>> make supporting more watchpoints very easy.
>>>
>>> With more than one watchpoint, exception handler need to know which
>>> DAWR caused the exception, and hw currently does not provide it. So
>>> we need sw logic for the same. To figure out which DAWR caused the
>>> exception, check all different combinations of user specified range,
>>> dawr address range, actual access range and dawrx constrains. For ex,
>>> if user specified range and actual access range overlaps but dawrx is
>>> configured for readonly watchpoint and the instruction is store, this
>>> DAWR must not have caused exception.
>>>
>>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>>> ---
>>> arch/powerpc/include/asm/processor.h | 2 +-
>>> arch/powerpc/include/asm/sstep.h | 2 +
>>> arch/powerpc/kernel/hw_breakpoint.c | 396 +++++++++++++++++++++------
>>> arch/powerpc/kernel/process.c | 3 -
>>> 4 files changed, 313 insertions(+), 90 deletions(-)
>>>
>>
>> [...]
>>
>>> -static bool
>>> -dar_range_overlaps(unsigned long dar, int size, struct
>>> arch_hw_breakpoint *info)
>>> +static bool dar_user_range_overlaps(unsigned long dar, int size,
>>> + struct arch_hw_breakpoint *info)
>>> {
>>> return ((dar <= info->address + info->len - 1) &&
>>> (dar + size - 1 >= info->address));
>>> }
>>
>> Here and several other places, I think it would be more clear if you
>> could avoid the - 1 :
>>
>> return ((dar < info->address + info->len) &&
>> (dar + size > info->address));
>
> Ok. see below...
>
>>
>>
>>> +static bool dar_in_hw_range(unsigned long dar, struct
>>> arch_hw_breakpoint *info)
>>> +{
>>> + unsigned long hw_start_addr, hw_end_addr;
>>> +
>>> + hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE);
>>> + hw_end_addr = ALIGN(info->address + info->len,
>>> HW_BREAKPOINT_SIZE) - 1;
>>> +
>>> + return ((hw_start_addr <= dar) && (hw_end_addr >= dar));
>>> +}
>>
>> hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE);
>>
>> return ((hw_start_addr <= dar) && (hw_end_addr > dar));
>
> I'm using -1 while calculating end address is to make it
> inclusive. If I don't use -1, the end address points to a
> location outside of actual range, i.e. it's not really an
> end address.
But that's what is done is several places, for instance:
https://elixir.bootlin.com/linux/v5.6/source/arch/powerpc/mm/dma-noncoherent.c#L22
https://elixir.bootlin.com/linux/v5.6/source/arch/powerpc/include/asm/book3s/32/kup.h#L92
In several places like this, end is outside of the range. My feeling is
that is helps with readability.
Christophe
^ permalink raw reply
* Re: [PATCH v2 13/16] powerpc/watchpoint: Prepare handler to handle more than one watcnhpoint
From: Ravi Bangoria @ 2020-04-01 9:23 UTC (permalink / raw)
To: Christophe Leroy
Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
mingo
In-Reply-To: <78559ff4-c2c3-e652-a906-8f40673b53d6@c-s.fr>
On 4/1/20 2:50 PM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 11:13, Ravi Bangoria a écrit :
>>
>>
>> On 4/1/20 12:20 PM, Christophe Leroy wrote:
>>>
>>>
>>> Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
>>>> Currently we assume that we have only one watchpoint supported by hw.
>>>> Get rid of that assumption and use dynamic loop instead. This should
>>>> make supporting more watchpoints very easy.
>>>>
>>>> With more than one watchpoint, exception handler need to know which
>>>> DAWR caused the exception, and hw currently does not provide it. So
>>>> we need sw logic for the same. To figure out which DAWR caused the
>>>> exception, check all different combinations of user specified range,
>>>> dawr address range, actual access range and dawrx constrains. For ex,
>>>> if user specified range and actual access range overlaps but dawrx is
>>>> configured for readonly watchpoint and the instruction is store, this
>>>> DAWR must not have caused exception.
>>>>
>>>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>>>> ---
>>>> arch/powerpc/include/asm/processor.h | 2 +-
>>>> arch/powerpc/include/asm/sstep.h | 2 +
>>>> arch/powerpc/kernel/hw_breakpoint.c | 396 +++++++++++++++++++++------
>>>> arch/powerpc/kernel/process.c | 3 -
>>>> 4 files changed, 313 insertions(+), 90 deletions(-)
>>>>
>>>
>>> [...]
>>>
>>>> -static bool
>>>> -dar_range_overlaps(unsigned long dar, int size, struct arch_hw_breakpoint *info)
>>>> +static bool dar_user_range_overlaps(unsigned long dar, int size,
>>>> + struct arch_hw_breakpoint *info)
>>>> {
>>>> return ((dar <= info->address + info->len - 1) &&
>>>> (dar + size - 1 >= info->address));
>>>> }
>>>
>>> Here and several other places, I think it would be more clear if you could avoid the - 1 :
>>>
>>> return ((dar < info->address + info->len) &&
>>> (dar + size > info->address));
>>
>> Ok. see below...
>>
>>>
>>>
>>>> +static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info)
>>>> +{
>>>> + unsigned long hw_start_addr, hw_end_addr;
>>>> +
>>>> + hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE);
>>>> + hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE) - 1;
>>>> +
>>>> + return ((hw_start_addr <= dar) && (hw_end_addr >= dar));
>>>> +}
>>>
>>> hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE);
>>>
>>> return ((hw_start_addr <= dar) && (hw_end_addr > dar));
>>
>> I'm using -1 while calculating end address is to make it
>> inclusive. If I don't use -1, the end address points to a
>> location outside of actual range, i.e. it's not really an
>> end address.
>
> But that's what is done is several places, for instance:
>
> https://elixir.bootlin.com/linux/v5.6/source/arch/powerpc/mm/dma-noncoherent.c#L22
>
> https://elixir.bootlin.com/linux/v5.6/source/arch/powerpc/include/asm/book3s/32/kup.h#L92
>
> In several places like this, end is outside of the range. My feeling is that is helps with readability.
Agreed, it helps with readability. Will change it.
Thanks for the links.
Ravi
^ permalink raw reply
* Re: [PATCH v3 1/1] ppc/crash: Reset spinlocks during crash
From: Peter Zijlstra @ 2020-04-01 9:26 UTC (permalink / raw)
To: Leonardo Bras
Cc: Greg Kroah-Hartman, linuxppc-dev, linux-kernel, Alexios Zavras,
Paul Mackerras, Thomas Gleixner, Enrico Weigelt
In-Reply-To: <20200401000020.590447-1-leonardo@linux.ibm.com>
On Tue, Mar 31, 2020 at 09:00:21PM -0300, Leonardo Bras wrote:
> During a crash, there is chance that the cpus that handle the NMI IPI
> are holding a spin_lock. If this spin_lock is needed by crashing_cpu it
> will cause a deadlock. (rtas.lock and printk logbuf_lock as of today)
>
> This is a problem if the system has kdump set up, given if it crashes
> for any reason kdump may not be saved for crash analysis.
>
> After NMI IPI is sent to all other cpus, force unlock all spinlocks
> needed for finishing crash routine.
>
> Signed-off-by: Leonardo Bras <leonardo@linux.ibm.com>
> @@ -129,6 +132,13 @@ static void crash_kexec_prepare_cpus(int cpu)
> /* Would it be better to replace the trap vector here? */
>
> if (atomic_read(&cpus_in_crash) >= ncpus) {
> + /*
> + * At this point no other CPU is running, and some of them may
> + * have been interrupted while holding one of the locks needed
> + * to complete crashing. Free them so there is no deadlock.
> + */
> + arch_spin_unlock(&logbuf_lock.raw_lock);
> + arch_spin_unlock(&rtas.lock);
> printk(KERN_EMERG "IPI complete\n");
> return;
> }
You might want to add a note to your asm/spinlock.h that you rely on
spin_unlock() unconditionally clearing a lock.
This isn't naturally true for all lock implementations. Consider ticket
locks, doing a surplus unlock will wreck your lock state in that case.
So anybody poking at the powerpc spinlock implementation had better know
you rely on this.
^ permalink raw reply
* Re: [PATCH v4 2/6] powerpc/idle: Add accessor function to always read latest idle PURR
From: Naveen N. Rao @ 2020-04-01 9:42 UTC (permalink / raw)
To: Gautham R. Shenoy, Kamalesh Babulal, Michael Ellerman,
Nathan Lynch, Vaidyanathan Srinivasan, Tyrel Datwyler
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1585308760-28792-3-git-send-email-ego@linux.vnet.ibm.com>
Hi Gautham,
Gautham R. Shenoy wrote:
> From: "Gautham R. Shenoy" <ego@linux.vnet.ibm.com>
>
> Currently when CPU goes idle, we take a snapshot of PURR via
> pseries_idle_prolog() which is used at the CPU idle exit to compute
> the idle PURR cycles via the function pseries_idle_epilog(). Thus,
> the value of idle PURR cycle thus read before pseries_idle_prolog() and
> after pseries_idle_epilog() is always correct.
>
> However, if we were to read the idle PURR cycles from an interrupt
> context between pseries_idle_prolog() and pseries_idle_epilog() (this will
> be done in a future patch), then, the value of the idle PURR thus read
> will not include the cycles spent in the most recent idle period.
>
> This patch addresses the issue by providing accessor function to read
> the idle PURR such such that it includes the cycles spent in the most
> recent idle period, if we read it between pseries_idle_prolog() and
> pseries_idle_epilog(). In order to achieve it, the patch saves the
> snapshot of PURR in pseries_idle_prolog() in a per-cpu variable,
> instead of on the stack, so that it can be accessed from an interrupt
> context.
>
> Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
> ---
> arch/powerpc/include/asm/idle.h | 47 +++++++++++++++++++++++++++-------
> arch/powerpc/platforms/pseries/setup.c | 7 +++--
> drivers/cpuidle/cpuidle-pseries.c | 15 +++++------
> 3 files changed, 47 insertions(+), 22 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/idle.h b/arch/powerpc/include/asm/idle.h
> index 32064a4c..d4bfb6a 100644
> --- a/arch/powerpc/include/asm/idle.h
> +++ b/arch/powerpc/include/asm/idle.h
> @@ -5,10 +5,27 @@
> #include <asm/paca.h>
>
> #ifdef CONFIG_PPC_PSERIES
> -static inline void pseries_idle_prolog(unsigned long *in_purr)
> +DECLARE_PER_CPU(u64, idle_entry_purr_snap);
> +
> +static inline void snapshot_purr_idle_entry(void)
> +{
> + *this_cpu_ptr(&idle_entry_purr_snap) = mfspr(SPRN_PURR);
> +}
> +
> +static inline void update_idle_purr_accounting(void)
> +{
> + u64 wait_cycles;
> + u64 in_purr = *this_cpu_ptr(&idle_entry_purr_snap);
> +
> + wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles);
> + wait_cycles += mfspr(SPRN_PURR) - in_purr;
> + get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles);
> +}
> +
> +static inline void pseries_idle_prolog(void)
> {
> ppc64_runlatch_off();
> - *in_purr = mfspr(SPRN_PURR);
> + snapshot_purr_idle_entry();
> /*
> * Indicate to the HV that we are idle. Now would be
> * a good time to find other work to dispatch.
> @@ -16,16 +33,28 @@ static inline void pseries_idle_prolog(unsigned long *in_purr)
> get_lppaca()->idle = 1;
> }
>
> -static inline void pseries_idle_epilog(unsigned long in_purr)
> +static inline void pseries_idle_epilog(void)
> {
> - u64 wait_cycles;
> -
> - wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles);
> - wait_cycles += mfspr(SPRN_PURR) - in_purr;
> - get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles);
> + update_idle_purr_accounting();
> get_lppaca()->idle = 0;
> -
> ppc64_runlatch_on();
> }
> +
> +static inline u64 read_this_idle_purr(void)
> +{
> + /*
> + * If we are reading from an idle context, update the
> + * idle-purr cycles corresponding to the last idle period.
> + * Since the idle context is not yet over, take a fresh
> + * snapshot of the idle-purr.
> + */
> + if (unlikely(get_lppaca()->idle == 1)) {
> + update_idle_purr_accounting();
> + snapshot_purr_idle_entry();
> + }
> +
> + return be64_to_cpu(get_lppaca()->wait_state_cycles);
> +}
> +
I think this and read_this_idle_spurr() from the next patch should be
moved to Patch 4/6, where they are actually used.
- Naveen
^ permalink raw reply
* Re: [PATCH v2 06/16] powerpc/watchpoint: Provide DAWR number to __set_breakpoint
From: Christophe Leroy @ 2020-04-01 9:44 UTC (permalink / raw)
To: Ravi Bangoria
Cc: apopple, mikey, peterz, fweisbec, oleg, npiggin, linux-kernel,
paulus, jolsa, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <d62efd40-4aa6-01e5-a669-3590b121c0f8@c-s.fr>
Le 01/04/2020 à 11:11, Christophe Leroy a écrit :
>
>
> Le 01/04/2020 à 10:57, Ravi Bangoria a écrit :
>>
>>
>> On 4/1/20 12:33 PM, Christophe Leroy wrote:
>>>
>>>
>>> Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
>>>> Introduce new parameter 'nr' to __set_breakpoint() which indicates
>>>> which DAWR should be programed. Also convert current_brk variable
>>>> to an array.
>>>>
>>>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>>>> ---
>>>> arch/powerpc/include/asm/debug.h | 2 +-
>>>> arch/powerpc/include/asm/hw_breakpoint.h | 2 +-
>>>> arch/powerpc/kernel/hw_breakpoint.c | 8 ++++----
>>>> arch/powerpc/kernel/process.c | 14 +++++++-------
>>>> arch/powerpc/kernel/signal.c | 2 +-
>>>> arch/powerpc/xmon/xmon.c | 2 +-
>>>> 6 files changed, 15 insertions(+), 15 deletions(-)
>>>>
>>>> diff --git a/arch/powerpc/include/asm/debug.h
>>>> b/arch/powerpc/include/asm/debug.h
>>>> index 7756026b95ca..6228935a8b64 100644
>>>> --- a/arch/powerpc/include/asm/debug.h
>>>> +++ b/arch/powerpc/include/asm/debug.h
>>>> @@ -45,7 +45,7 @@ static inline int debugger_break_match(struct
>>>> pt_regs *regs) { return 0; }
>>>> static inline int debugger_fault_handler(struct pt_regs *regs) {
>>>> return 0; }
>>>> #endif
>>>> -void __set_breakpoint(struct arch_hw_breakpoint *brk);
>>>> +void __set_breakpoint(struct arch_hw_breakpoint *brk, int nr);
>>>
>>> Same, I think it would make more sense to have nr as first argument.
>>
>> Sorry, didn't get your point. How will that help?
>>
>
> Well, it is a tiny detail but for me it is more natural to first tel
> which element you modify before telling how you modify it.
>
And the second advantage is that when you have a function
get_something() paired with you set_something(), you can then have it as
first argument in both functions.
void set_something(int nr, type something)
type get_something(int nr)
But again, that's detail, so up to you.
Christophe
^ permalink raw reply
* Re: [PATCH v4 5/6] Documentation: Document sysfs interfaces purr, spurr, idle_purr, idle_spurr
From: Naveen N. Rao @ 2020-04-01 9:45 UTC (permalink / raw)
To: Gautham R. Shenoy, Kamalesh Babulal, Michael Ellerman,
Nathan Lynch, Vaidyanathan Srinivasan, Tyrel Datwyler
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1585308760-28792-6-git-send-email-ego@linux.vnet.ibm.com>
Gautham R. Shenoy wrote:
> From: "Gautham R. Shenoy" <ego@linux.vnet.ibm.com>
>
> Add documentation for the following sysfs interfaces:
> /sys/devices/system/cpu/cpuX/purr
> /sys/devices/system/cpu/cpuX/spurr
> /sys/devices/system/cpu/cpuX/idle_purr
> /sys/devices/system/cpu/cpuX/idle_spurr
>
> Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
> ---
> Documentation/ABI/testing/sysfs-devices-system-cpu | 39 ++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index 2e0e3b4..bc07677 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -580,3 +580,42 @@ Description: Secure Virtual Machine
> If 1, it means the system is using the Protected Execution
> Facility in POWER9 and newer processors. i.e., it is a Secure
> Virtual Machine.
> +
> +What: /sys/devices/system/cpu/cpuX/purr
> +Date: Apr 2005
> +Contact: Linux for PowerPC mailing list <linuxppc-dev@ozlabs.org>
> +Description: PURR ticks for this CPU since the system boot.
> +
> + The Processor Utilization Resources Register (PURR) is
> + a 64-bit counter which provides an estimate of the
> + resources used by the CPU thread. The contents of this
> + register increases monotonically. This sysfs interface
> + exposes the number of PURR ticks for cpuX.
> +
> +What: /sys/devices/system/cpu/cpuX/spurr
> +Date: Dec 2006
> +Contact: Linux for PowerPC mailing list <linuxppc-dev@ozlabs.org>
> +Description: SPURR ticks for this CPU since the system boot.
> +
> + The Scaled Processor Utilization Resources Register
> + (SPURR) is a 64-bit counter that provides a frequency
> + invariant estimate of the resources used by the CPU
> + thread. The contents of this register increases
> + monotonically. This sysfs interface exposes the number
> + of SPURR ticks for cpuX.
> +
> +What: /sys/devices/system/cpu/cpuX/idle_purr
> +Date: Mar 2020
> +Contact: Linux for PowerPC mailing list <linuxppc-dev@ozlabs.org>
> +Description: PURR ticks for cpuX when it was idle.
> +
> + This sysfs interface exposes the number of PURR ticks
> + for cpuX when it was idle.
> +
> +What: /sys/devices/system/cpu/cpuX/idle_spurr
> +Date: Mar 2020
> +Contact: Linux for PowerPC mailing list <linuxppc-dev@ozlabs.org>
> +Description: SPURR ticks for cpuX when it was idle.
> +
> + This sysfs interface exposes the number of SPURR ticks
> + for cpuX when it was idle.
Apart from the minor comment on patches 2 and 3, from the sysfs
interface standpoint, for patches 1 to 5:
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
- Naveen
^ permalink raw reply
* Re: [PATCH v4 6/6] pseries/sysfs: Minimise IPI noise while reading [idle_][s]purr
From: Naveen N. Rao @ 2020-04-01 9:58 UTC (permalink / raw)
To: Gautham R. Shenoy, Kamalesh Babulal, Michael Ellerman,
Nathan Lynch, Vaidyanathan Srinivasan, Tyrel Datwyler
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1585308760-28792-7-git-send-email-ego@linux.vnet.ibm.com>
Gautham R. Shenoy wrote:
> From: "Gautham R. Shenoy" <ego@linux.vnet.ibm.com>
>
> Currently purr, spurr, idle_purr, idle_spurr are exposed for every CPU
> via the sysfs interface
> /sys/devices/system/cpu/cpuX/[idle_][s]purr. Each sysfs read currently
> generates an IPI to obtain the desired value from the target CPU X.
> Since these aforementioned sysfs are typically read one after another,
> we end up generating 4 IPIs per CPU in a short duration.
>
> In order to minimize the IPI noise, this patch caches the values of
> all the four entities whenever one of them is read. If subsequently
> any of these are read within the next 10ms, the cached value is
> returned. With this, we will generate at most one IPI every 10ms for
> every CPU.
>
> Test-results: While reading the four sysfs files back-to-back for a
> given CPU every second for 100 seconds.
>
> Without the patch:
> 16 [XICS 2 Edge IPI] = 422 times
> DBL [Doorbell interrupts] = 13 times
> Total : 435 IPIs.
>
> With the patch:
> 16 [XICS 2 Edge IPI] = 111 times
> DBL [Doorbell interrupts] = 17 times
> Total : 128 IPIs.
>
> Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
> ---
> arch/powerpc/kernel/sysfs.c | 117 ++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 97 insertions(+), 20 deletions(-)
>
> diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
> index 571b325..bd92023 100644
> --- a/arch/powerpc/kernel/sysfs.c
> +++ b/arch/powerpc/kernel/sysfs.c
> @@ -586,8 +586,6 @@ void ppc_enable_pmcs(void)
> * SPRs which are not related to PMU.
> */
> #ifdef CONFIG_PPC64
> -SYSFS_SPRSETUP(purr, SPRN_PURR);
> -SYSFS_SPRSETUP(spurr, SPRN_SPURR);
> SYSFS_SPRSETUP(pir, SPRN_PIR);
> SYSFS_SPRSETUP(tscr, SPRN_TSCR);
>
> @@ -596,8 +594,6 @@ void ppc_enable_pmcs(void)
> enable write when needed with a separate function.
> Lets be conservative and default to pseries.
> */
> -static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
> -static DEVICE_ATTR(purr, 0400, show_purr, store_purr);
> static DEVICE_ATTR(pir, 0400, show_pir, NULL);
> static DEVICE_ATTR(tscr, 0600, show_tscr, store_tscr);
> #endif /* CONFIG_PPC64 */
> @@ -761,22 +757,110 @@ static void create_svm_file(void)
> }
> #endif /* CONFIG_PPC_SVM */
>
> +#ifdef CONFIG_PPC64
> +/*
> + * The duration (in ms) from the last IPI to the target CPU until
> + * which a cached value of purr, spurr, idle_purr, idle_spurr can be
> + * reported to the user on a corresponding sysfs file read. Beyond
> + * this duration, fresh values need to be obtained by sending IPIs to
> + * the target CPU when the sysfs files are read.
> + */
> +static unsigned long util_stats_staleness_tolerance_ms = 10;
This is a nice optimization for our use in lparstat, though I have a
concern below.
> +struct util_acct_stats {
> + u64 latest_purr;
> + u64 latest_spurr;
> +#ifdef CONFIG_PPC_PSERIES
> + u64 latest_idle_purr;
> + u64 latest_idle_spurr;
> +#endif
You can probably drop the 'latest_' prefix.
> + unsigned long last_update_jiffies;
> +};
> +
> +DEFINE_PER_CPU(struct util_acct_stats, util_acct_stats);
Per snowpatch, this should be static, and so should get_util_stats_ptr()
below:
https://openpower.xyz/job/snowpatch/job/snowpatch-linux-sparse/16601//artifact/linux/report.txt
> +
> +static void update_util_acct_stats(void *ptr)
> +{
> + struct util_acct_stats *stats = ptr;
> +
> + stats->latest_purr = mfspr(SPRN_PURR);
> + stats->latest_spurr = mfspr(SPRN_SPURR);
> #ifdef CONFIG_PPC_PSERIES
> -static void read_idle_purr(void *val)
> + stats->latest_idle_purr = read_this_idle_purr();
> + stats->latest_idle_spurr = read_this_idle_spurr();
> +#endif
> + stats->last_update_jiffies = jiffies;
> +}
> +
> +struct util_acct_stats *get_util_stats_ptr(int cpu)
> +{
> + struct util_acct_stats *stats = per_cpu_ptr(&util_acct_stats, cpu);
> + unsigned long delta_jiffies;
> +
> + delta_jiffies = jiffies - stats->last_update_jiffies;
> +
> + /*
> + * If we have a recent enough data, reuse that instead of
> + * sending an IPI.
> + */
> + if (jiffies_to_msecs(delta_jiffies) < util_stats_staleness_tolerance_ms)
> + return stats;
> +
> + smp_call_function_single(cpu, update_util_acct_stats, stats, 1);
> + return stats;
> +}
> +
> +static ssize_t show_purr(struct device *dev,
> + struct device_attribute *attr, char *buf)
> {
> - u64 *ret = val;
> + struct cpu *cpu = container_of(dev, struct cpu, dev);
> + struct util_acct_stats *stats;
>
> - *ret = read_this_idle_purr();
> + stats = get_util_stats_ptr(cpu->dev.id);
> + return sprintf(buf, "%llx\n", stats->latest_purr);
This alters the behavior of the current sysfs purr file. I am not sure
if it is reasonable to return the same PURR value across a 10ms window.
I wonder if we should introduce a sysctl interface to control
thresholding. It can default to 0, which disables thresholding so that
the existing behavior continues. Applications (lparstat) can optionally
set it to suit their use.
- Naveen
^ permalink raw reply
* [PATCH v2] powerpc/fadump: fix race between pstore write and fadump crash trigger
From: Sourabh Jain @ 2020-04-01 10:02 UTC (permalink / raw)
To: mpe; +Cc: Sourabh Jain, mahesh, linux-kernel, hbathini, linuxppc-dev
When we enter into fadump crash path via system reset we fail to update
the pstore.
On the system reset path we first update the pstore then we go for fadump
crash. But the problem here is when all the CPUs try to get the pstore
lock to initiate the pstore write, only one CPUs will acquire the lock
and proceed with the pstore write. Since it in NMI context CPUs that fail
to get lock do not wait for their turn to write to the pstore and simply
proceed with the next operation which is fadump crash. One of the CPU who
proceeded with fadump crash path triggers the crash and does not wait for
the CPU who gets the pstore lock to complete the pstore update.
Timeline diagram to depicts the sequence of events that leads to an
unsuccessful pstore update when we hit fadump crash path via system reset.
1 2 3 ... n CPU Threads
| | | |
| | | |
Reached to -->|--->|---->| ----------->|
system reset | | | |
path | | | |
| | | |
Try to -->|--->|---->|------------>|
acquire the | | | |
pstore lock | | | |
| | | |
| | | |
Got the -->| +->| | |<-+
pstore lock | | | | | |--> Didn't get the
| --------------------------+ lock and moving
| | | | ahead on fadump
| | | | crash path
| | | |
Begins the -->| | | |
process to | | | |<-- Got the chance to
update the | | | | trigger the crash
pstore | -> | | ... <- |
| | | | | |
| | | | | |<-- Triggers the
| | | | | | crash
| | | | | | ^
| | | | | | |
Writing to -->| | | | | | |
pstore | | | | | | |
| | |
^ |__________________| |
| CPU Relax |
| |
+-----------------------------------------+
|
v
Race: crash triggered before pstore
update completes
To avoid the race between the CPU who proceeds with the pstore and the CPU
who triggers the crash, a delay of 500 milliseconds is added on fadump
crash path to allow pstore update to complete before we trigger the crash.
Signed-off-by: Sourabh Jain <sourabhjain@linux.ibm.com>
---
arch/powerpc/kernel/fadump.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
---
Changelog:
v1 -> v2
- crash delay has been increased to 500 milliseconds
- race happens in NMI context so updated the commit
message to convey the same.
---
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index ff0114aeba9b..471171c40e64 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -32,6 +32,16 @@
#include <asm/fadump-internal.h>
#include <asm/setup.h>
+
+/*
+ * The CPU who acquired the lock to trigger the fadump crash should
+ * wait for other CPUs to complete their tasks (for example updating
+ * pstore) before triggering the crash.
+ *
+ * The timeout is in milliseconds.
+ */
+#define CRASH_TIMEOUT 500
+
static struct fw_dump fw_dump;
static void __init fadump_reserve_crash_area(u64 base);
@@ -634,6 +644,14 @@ void crash_fadump(struct pt_regs *regs, const char *str)
fdh->online_mask = *cpu_online_mask;
+ /*
+ * If we reached here via system reset path then let's
+ * wait for other CPUs to complete their task like pstore
+ * update.
+ */
+ if (TRAP(regs) == 0x100)
+ mdelay(CRASH_TIMEOUT);
+
fw_dump.ops->fadump_trigger(fdh, str);
}
--
2.21.1
^ permalink raw reply related
* Re: [PATCH v4 03/16] powerpc: Use a datatype for instructions
From: Balamuruhan S @ 2020-04-01 10:32 UTC (permalink / raw)
To: Jordan Niethe, linuxppc-dev; +Cc: alistair, dja, npiggin
In-Reply-To: <20200320051809.24332-4-jniethe5@gmail.com>
On Fri, 2020-03-20 at 16:17 +1100, Jordan Niethe wrote:
> Currently unsigned ints are used to represent instructions on powerpc.
> This has worked well as instructions have always been 4 byte words.
> However, a future ISA version will introduce some changes to
> instructions that mean this scheme will no longer work as well. This
> change is Prefixed Instructions. A prefixed instruction is made up of a
> word prefix followed by a word suffix to make an 8 byte double word
> instruction. No matter the endianess of the system the prefix always
> comes first. Prefixed instructions are only planned for powerpc64.
>
> Introduce a ppc_inst type to represent both prefixed and word
> instructions on powerpc64 while keeping it possible to exclusively have
> word instructions on powerpc32, A latter patch will expand the type to
> include prefixed instructions but for now just typedef it to a u32.
>
> Later patches will introduce helper functions and macros for
> manipulating the instructions so that powerpc64 and powerpc32 might
> maintain separate type definitions.
>
> Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
> ---
> arch/powerpc/include/asm/code-patching.h | 31 +++++------
> arch/powerpc/include/asm/inst.h | 53 +++++++++++++++++++
> arch/powerpc/include/asm/sstep.h | 5 +-
> arch/powerpc/kernel/align.c | 2 +-
> arch/powerpc/kernel/hw_breakpoint.c | 3 +-
> arch/powerpc/kernel/kprobes.c | 2 +-
> arch/powerpc/kernel/mce_power.c | 5 +-
> arch/powerpc/kernel/optprobes.c | 10 ++--
> arch/powerpc/kernel/trace/ftrace.c | 66 ++++++++++++------------
> arch/powerpc/kvm/emulate_loadstore.c | 1 +
> arch/powerpc/lib/code-patching.c | 54 +++++++++----------
> arch/powerpc/lib/sstep.c | 4 +-
> arch/powerpc/lib/test_emulate_step.c | 9 ++--
> arch/powerpc/xmon/xmon.c | 12 ++---
> 14 files changed, 160 insertions(+), 97 deletions(-)
> create mode 100644 arch/powerpc/include/asm/inst.h
>
> diff --git a/arch/powerpc/include/asm/code-patching.h
> b/arch/powerpc/include/asm/code-patching.h
> index 898b54262881..cb5106f92d67 100644
> --- a/arch/powerpc/include/asm/code-patching.h
> +++ b/arch/powerpc/include/asm/code-patching.h
> @@ -11,6 +11,7 @@
> #include <linux/string.h>
> #include <linux/kallsyms.h>
> #include <asm/asm-compat.h>
> +#include <asm/inst.h>
>
> /* Flags for create_branch:
> * "b" == create_branch(addr, target, 0);
> @@ -22,27 +23,27 @@
> #define BRANCH_ABSOLUTE 0x2
>
> bool is_offset_in_branch_range(long offset);
> -unsigned int create_branch(const unsigned int *addr,
> +ppc_inst create_branch(const ppc_inst *addr,
> unsigned long target, int flags);
> -unsigned int create_cond_branch(const unsigned int *addr,
> +unsigned int create_cond_branch(const ppc_inst *addr,
> unsigned long target, int flags);
> -int patch_branch(unsigned int *addr, unsigned long target, int flags);
> -int patch_instruction(unsigned int *addr, unsigned int instr);
> -int raw_patch_instruction(unsigned int *addr, unsigned int instr);
> +int patch_branch(ppc_inst *addr, unsigned long target, int flags);
> +int patch_instruction(ppc_inst *addr, ppc_inst instr);
we need to handle this change for its user in epapr_paravirt.c,
arch/powerpc/kernel/epapr_paravirt.c: In function 'early_init_dt_scan_epapr':
arch/powerpc/kernel/epapr_paravirt.c:40:48: error: incompatible type for
argument 2 of 'patch_instruction'
40 | patch_instruction(epapr_hypercall_start + i, inst);
| ^~~~
| |
| u32 {aka unsigned int}
In file included from arch/powerpc/kernel/epapr_paravirt.c:12:
./arch/powerpc/include/asm/code-patching.h:31:44: note: expected 'ppc_inst'
{aka 'struct ppc_inst'} but argument is of type 'u32' {aka 'unsigned int'}
31 | int patch_instruction(void *addr, ppc_inst instr);
| ~~~~~~~~~^~~~~
make[2]: *** [scripts/Makefile.build:268: arch/powerpc/kernel/epapr_paravirt.o]
Error 1
make[1]: *** [scripts/Makefile.build:505: arch/powerpc/kernel] Error 2
make: *** [Makefile:1683: arch/powerpc] Error 2
-- Bala
> +int raw_patch_instruction(ppc_inst *addr, ppc_inst instr);
>
> static inline unsigned long patch_site_addr(s32 *site)
> {
> return (unsigned long)site + *site;
> }
>
> -static inline int patch_instruction_site(s32 *site, unsigned int instr)
> +static inline int patch_instruction_site(s32 *site, ppc_inst instr)
> {
> - return patch_instruction((unsigned int *)patch_site_addr(site), instr);
> + return patch_instruction((ppc_inst *)patch_site_addr(site), instr);
> }
>
> static inline int patch_branch_site(s32 *site, unsigned long target, int
> flags)
> {
> - return patch_branch((unsigned int *)patch_site_addr(site), target,
> flags);
> + return patch_branch((ppc_inst *)patch_site_addr(site), target, flags);
> }
>
> static inline int modify_instruction(unsigned int *addr, unsigned int clr,
> @@ -56,13 +57,13 @@ static inline int modify_instruction_site(s32 *site,
> unsigned int clr, unsigned
> return modify_instruction((unsigned int *)patch_site_addr(site), clr,
> set);
> }
>
> -int instr_is_relative_branch(unsigned int instr);
> -int instr_is_relative_link_branch(unsigned int instr);
> -int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr);
> -unsigned long branch_target(const unsigned int *instr);
> -unsigned int translate_branch(const unsigned int *dest,
> - const unsigned int *src);
> -extern bool is_conditional_branch(unsigned int instr);
> +int instr_is_relative_branch(ppc_inst instr);
> +int instr_is_relative_link_branch(ppc_inst instr);
> +int instr_is_branch_to_addr(const ppc_inst *instr, unsigned long addr);
> +unsigned long branch_target(const ppc_inst *instr);
> +ppc_inst translate_branch(const ppc_inst *dest,
> + const ppc_inst *src);
> +extern bool is_conditional_branch(ppc_inst instr);
> #ifdef CONFIG_PPC_BOOK3E_64
> void __patch_exception(int exc, unsigned long addr);
> #define patch_exception(exc, name) do { \
> diff --git a/arch/powerpc/include/asm/inst.h
> b/arch/powerpc/include/asm/inst.h
> new file mode 100644
> index 000000000000..7c8596ee411e
> --- /dev/null
> +++ b/arch/powerpc/include/asm/inst.h
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef _ASM_INST_H
> +#define _ASM_INST_H
> +
> +/*
> + * Instruction data type for POWER
> + */
> +
> +typedef u32 ppc_inst;
> +
> +#define PPC_INST(x) (x)
> +
> +static inline int ppc_inst_len(ppc_inst x)
> +{
> + return sizeof(ppc_inst);
> +}
> +
> +static inline int ppc_inst_opcode(ppc_inst x)
> +{
> + return x >> 26;
> +}
> +
> +static inline u32 ppc_inst_word(ppc_inst x)
> +{
> + return x;
> +}
> +
> +static inline ppc_inst ppc_inst_read(const ppc_inst *ptr)
> +{
> + return *(ppc_inst *)ptr;
> +}
> +
> +static inline void ppc_inst_write(void *ptr, ppc_inst x)
> +{
> + *(ppc_inst *)ptr = x;
> +}
> +
> +static inline bool ppc_inst_equal(ppc_inst x, ppc_inst y)
> +{
> + return x == y;
> +}
> +
> +static inline bool ppc_inst_null(ppc_inst x)
> +{
> + return x == 0;
> +}
> +
> +static inline u32 ppc_inst_mask(ppc_inst x, u32 mask)
> +{
> + return ppc_inst_word(x) & mask;
> +}
> +
> +#endif /* _ASM_INST_H */
> diff --git a/arch/powerpc/include/asm/sstep.h
> b/arch/powerpc/include/asm/sstep.h
> index 769f055509c9..9353916fcba7 100644
> --- a/arch/powerpc/include/asm/sstep.h
> +++ b/arch/powerpc/include/asm/sstep.h
> @@ -2,6 +2,7 @@
> /*
> * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
> */
> +#include <asm/inst.h>
>
> struct pt_regs;
>
> @@ -132,7 +133,7 @@ union vsx_reg {
> * otherwise.
> */
> extern int analyse_instr(struct instruction_op *op, const struct pt_regs
> *regs,
> - unsigned int instr);
> + ppc_inst instr);
>
> /*
> * Emulate an instruction that can be executed just by updating
> @@ -149,7 +150,7 @@ void emulate_update_regs(struct pt_regs *reg, struct
> instruction_op *op);
> * 0 if it could not be emulated, or -1 for an instruction that
> * should not be emulated (rfid, mtmsrd clearing MSR_RI, etc.).
> */
> -extern int emulate_step(struct pt_regs *regs, unsigned int instr);
> +extern int emulate_step(struct pt_regs *regs, ppc_inst instr);
>
> /*
> * Emulate a load or store instruction by reading/writing the
> diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
> index 92045ed64976..34594aaa44de 100644
> --- a/arch/powerpc/kernel/align.c
> +++ b/arch/powerpc/kernel/align.c
> @@ -293,7 +293,7 @@ static int emulate_spe(struct pt_regs *regs, unsigned int
> reg,
>
> int fix_alignment(struct pt_regs *regs)
> {
> - unsigned int instr;
> + ppc_inst instr;
> struct instruction_op op;
> int r, type;
>
> diff --git a/arch/powerpc/kernel/hw_breakpoint.c
> b/arch/powerpc/kernel/hw_breakpoint.c
> index 2462cd7c565c..06b97353d231 100644
> --- a/arch/powerpc/kernel/hw_breakpoint.c
> +++ b/arch/powerpc/kernel/hw_breakpoint.c
> @@ -24,6 +24,7 @@
> #include <asm/debug.h>
> #include <asm/debugfs.h>
> #include <asm/hvcall.h>
> +#include <asm/inst.h>
> #include <linux/uaccess.h>
>
> /*
> @@ -243,7 +244,7 @@ dar_range_overlaps(unsigned long dar, int size, struct
> arch_hw_breakpoint *info)
> static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
> struct arch_hw_breakpoint *info)
> {
> - unsigned int instr = 0;
> + ppc_inst instr = 0;
> int ret, type, size;
> struct instruction_op op;
> unsigned long addr = info->address;
> diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
> index 337516df17d4..e7205adc9820 100644
> --- a/arch/powerpc/kernel/kprobes.c
> +++ b/arch/powerpc/kernel/kprobes.c
> @@ -225,7 +225,7 @@ NOKPROBE_SYMBOL(arch_prepare_kretprobe);
> static int try_to_emulate(struct kprobe *p, struct pt_regs *regs)
> {
> int ret;
> - unsigned int insn = *p->ainsn.insn;
> + ppc_inst insn = *p->ainsn.insn;
>
> /* regs->nip is also adjusted if emulate_step returns 1 */
> ret = emulate_step(regs, insn);
> diff --git a/arch/powerpc/kernel/mce_power.c
> b/arch/powerpc/kernel/mce_power.c
> index 1cbf7f1a4e3d..e65616bb3a3e 100644
> --- a/arch/powerpc/kernel/mce_power.c
> +++ b/arch/powerpc/kernel/mce_power.c
> @@ -20,6 +20,7 @@
> #include <asm/sstep.h>
> #include <asm/exception-64s.h>
> #include <asm/extable.h>
> +#include <asm/inst.h>
>
> /*
> * Convert an address related to an mm to a PFN. NOTE: we are in real
> @@ -365,7 +366,7 @@ static int mce_find_instr_ea_and_phys(struct pt_regs
> *regs, uint64_t *addr,
> * in real-mode is tricky and can lead to recursive
> * faults
> */
> - int instr;
> + ppc_inst instr;
> unsigned long pfn, instr_addr;
> struct instruction_op op;
> struct pt_regs tmp = *regs;
> @@ -373,7 +374,7 @@ static int mce_find_instr_ea_and_phys(struct pt_regs
> *regs, uint64_t *addr,
> pfn = addr_to_pfn(regs, regs->nip);
> if (pfn != ULONG_MAX) {
> instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK);
> - instr = *(unsigned int *)(instr_addr);
> + instr = *(ppc_inst *)(instr_addr);
> if (!analyse_instr(&op, &tmp, instr)) {
> pfn = addr_to_pfn(regs, op.ea);
> *addr = op.ea;
> diff --git a/arch/powerpc/kernel/optprobes.c
> b/arch/powerpc/kernel/optprobes.c
> index 024f7aad1952..f5e8cce438a3 100644
> --- a/arch/powerpc/kernel/optprobes.c
> +++ b/arch/powerpc/kernel/optprobes.c
> @@ -189,8 +189,8 @@ void patch_imm64_load_insns(unsigned long val,
> kprobe_opcode_t *addr)
>
> int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe
> *p)
> {
> - kprobe_opcode_t *buff, branch_op_callback, branch_emulate_step;
> - kprobe_opcode_t *op_callback_addr, *emulate_step_addr;
> + ppc_inst branch_op_callback, branch_emulate_step;
> + kprobe_opcode_t *op_callback_addr, *emulate_step_addr, *buff;
> long b_offset;
> unsigned long nip, size;
> int rc, i;
> @@ -251,11 +251,11 @@ int arch_prepare_optimized_kprobe(struct
> optimized_kprobe *op, struct kprobe *p)
> goto error;
> }
>
> - branch_op_callback = create_branch((unsigned int *)buff +
> TMPL_CALL_HDLR_IDX,
> + branch_op_callback = create_branch((ppc_inst *)buff +
> TMPL_CALL_HDLR_IDX,
> (unsigned long)op_callback_addr,
> BRANCH_SET_LINK);
>
> - branch_emulate_step = create_branch((unsigned int *)buff +
> TMPL_EMULATE_IDX,
> + branch_emulate_step = create_branch((ppc_inst *)buff +
> TMPL_EMULATE_IDX,
> (unsigned long)emulate_step_addr,
> BRANCH_SET_LINK);
>
> @@ -316,7 +316,7 @@ void arch_optimize_kprobes(struct list_head *oplist)
> memcpy(op->optinsn.copied_insn, op->kp.addr,
> RELATIVEJUMP_SIZE);
> patch_instruction(op->kp.addr,
> - create_branch((unsigned int *)op->kp.addr,
> + create_branch((ppc_inst *)op->kp.addr,
> (unsigned long)op->optinsn.insn, 0));
> list_del_init(&op->list);
> }
> diff --git a/arch/powerpc/kernel/trace/ftrace.c
> b/arch/powerpc/kernel/trace/ftrace.c
> index 7ea0ca044b65..5787ccffb4df 100644
> --- a/arch/powerpc/kernel/trace/ftrace.c
> +++ b/arch/powerpc/kernel/trace/ftrace.c
> @@ -27,6 +27,7 @@
> #include <asm/code-patching.h>
> #include <asm/ftrace.h>
> #include <asm/syscall.h>
> +#include <asm/inst.h>
>
>
> #ifdef CONFIG_DYNAMIC_FTRACE
> @@ -40,23 +41,23 @@
> #define NUM_FTRACE_TRAMPS 8
> static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS];
>
> -static unsigned int
> +static ppc_inst
> ftrace_call_replace(unsigned long ip, unsigned long addr, int link)
> {
> - unsigned int op;
> + ppc_inst op;
>
> addr = ppc_function_entry((void *)addr);
>
> /* if (link) set op to 'bl' else 'b' */
> - op = create_branch((unsigned int *)ip, addr, link ? 1 : 0);
> + op = create_branch((ppc_inst *)ip, addr, link ? 1 : 0);
>
> return op;
> }
>
> static int
> -ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new)
> +ftrace_modify_code(unsigned long ip, ppc_inst old, ppc_inst new)
> {
> - unsigned int replaced;
> + ppc_inst replaced;
>
> /*
> * Note:
> @@ -78,7 +79,7 @@ ftrace_modify_code(unsigned long ip, unsigned int old,
> unsigned int new)
> }
>
> /* replace the text with the new text */
> - if (patch_instruction((unsigned int *)ip, new))
> + if (patch_instruction((ppc_inst *)ip, new))
> return -EPERM;
>
> return 0;
> @@ -87,25 +88,25 @@ ftrace_modify_code(unsigned long ip, unsigned int old,
> unsigned int new)
> /*
> * Helper functions that are the same for both PPC64 and PPC32.
> */
> -static int test_24bit_addr(unsigned long ip, unsigned long addr)
> +static ppc_inst test_24bit_addr(unsigned long ip, unsigned long addr)
> {
> addr = ppc_function_entry((void *)addr);
>
> /* use the create_branch to verify that this offset can be branched */
> - return create_branch((unsigned int *)ip, addr, 0);
> + return create_branch((ppc_inst *)ip, addr, 0);
> }
>
> -static int is_bl_op(unsigned int op)
> +static int is_bl_op(ppc_inst op)
> {
> return (op & 0xfc000003) == 0x48000001;
> }
>
> -static int is_b_op(unsigned int op)
> +static int is_b_op(ppc_inst op)
> {
> return (op & 0xfc000003) == 0x48000000;
> }
>
> -static unsigned long find_bl_target(unsigned long ip, unsigned int op)
> +static unsigned long find_bl_target(unsigned long ip, ppc_inst op)
> {
> int offset;
>
> @@ -125,7 +126,7 @@ __ftrace_make_nop(struct module *mod,
> {
> unsigned long entry, ptr, tramp;
> unsigned long ip = rec->ip;
> - unsigned int op, pop;
> + ppc_inst op, pop;
>
> /* read where this goes */
> if (probe_kernel_read(&op, (void *)ip, sizeof(int))) {
> @@ -204,7 +205,7 @@ __ftrace_make_nop(struct module *mod,
> }
> #endif /* CONFIG_MPROFILE_KERNEL */
>
> - if (patch_instruction((unsigned int *)ip, pop)) {
> + if (patch_instruction((ppc_inst *)ip, pop)) {
> pr_err("Patching NOP failed.\n");
> return -EPERM;
> }
> @@ -217,7 +218,7 @@ static int
> __ftrace_make_nop(struct module *mod,
> struct dyn_ftrace *rec, unsigned long addr)
> {
> - unsigned int op;
> + ppc_inst op;
> unsigned int jmp[4];
> unsigned long ip = rec->ip;
> unsigned long tramp;
> @@ -276,7 +277,7 @@ __ftrace_make_nop(struct module *mod,
>
> op = PPC_INST_NOP;
>
> - if (patch_instruction((unsigned int *)ip, op))
> + if (patch_instruction((ppc_inst *)ip, op))
> return -EPERM;
>
> return 0;
> @@ -322,7 +323,8 @@ static int add_ftrace_tramp(unsigned long tramp)
> */
> static int setup_mcount_compiler_tramp(unsigned long tramp)
> {
> - int i, op;
> + int i;
> + ppc_inst op;
> unsigned long ptr;
> static unsigned long ftrace_plt_tramps[NUM_FTRACE_TRAMPS];
>
> @@ -388,7 +390,7 @@ static int setup_mcount_compiler_tramp(unsigned long
> tramp)
> static int __ftrace_make_nop_kernel(struct dyn_ftrace *rec, unsigned long
> addr)
> {
> unsigned long tramp, ip = rec->ip;
> - unsigned int op;
> + ppc_inst op;
>
> /* Read where this goes */
> if (probe_kernel_read(&op, (void *)ip, sizeof(int))) {
> @@ -416,7 +418,7 @@ static int __ftrace_make_nop_kernel(struct dyn_ftrace
> *rec, unsigned long addr)
> }
> }
>
> - if (patch_instruction((unsigned int *)ip, PPC_INST_NOP)) {
> + if (patch_instruction((ppc_inst *)ip, PPC_INST_NOP)) {
> pr_err("Patching NOP failed.\n");
> return -EPERM;
> }
> @@ -428,7 +430,7 @@ int ftrace_make_nop(struct module *mod,
> struct dyn_ftrace *rec, unsigned long addr)
> {
> unsigned long ip = rec->ip;
> - unsigned int old, new;
> + ppc_inst old, new;
>
> /*
> * If the calling address is more that 24 bits away,
> @@ -481,7 +483,7 @@ int ftrace_make_nop(struct module *mod,
> */
> #ifndef CONFIG_MPROFILE_KERNEL
> static int
> -expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1)
> +expected_nop_sequence(void *ip, ppc_inst op0, ppc_inst op1)
> {
> /*
> * We expect to see:
> @@ -510,7 +512,7 @@ expected_nop_sequence(void *ip, unsigned int op0,
> unsigned int op1)
> static int
> __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> {
> - unsigned int op[2];
> + ppc_inst op[2];
> void *ip = (void *)rec->ip;
> unsigned long entry, ptr, tramp;
> struct module *mod = rec->arch.mod;
> @@ -574,7 +576,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long
> addr)
> static int
> __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> {
> - unsigned int op;
> + ppc_inst op;
> unsigned long ip = rec->ip;
>
> /* read where this goes */
> @@ -594,7 +596,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long
> addr)
> }
>
> /* create the branch to the trampoline */
> - op = create_branch((unsigned int *)ip,
> + op = create_branch((ppc_inst *)ip,
> rec->arch.mod->arch.tramp, BRANCH_SET_LINK);
> if (!op) {
> pr_err("REL24 out of range!\n");
> @@ -613,7 +615,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long
> addr)
>
> static int __ftrace_make_call_kernel(struct dyn_ftrace *rec, unsigned long
> addr)
> {
> - unsigned int op;
> + ppc_inst op;
> void *ip = (void *)rec->ip;
> unsigned long tramp, entry, ptr;
>
> @@ -661,7 +663,7 @@ static int __ftrace_make_call_kernel(struct dyn_ftrace
> *rec, unsigned long addr)
> int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> {
> unsigned long ip = rec->ip;
> - unsigned int old, new;
> + ppc_inst old, new;
>
> /*
> * If the calling address is more that 24 bits away,
> @@ -700,7 +702,7 @@ static int
> __ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
> unsigned long addr)
> {
> - unsigned int op;
> + ppc_inst op;
> unsigned long ip = rec->ip;
> unsigned long entry, ptr, tramp;
> struct module *mod = rec->arch.mod;
> @@ -748,7 +750,7 @@ __ftrace_modify_call(struct dyn_ftrace *rec, unsigned
> long old_addr,
> /* The new target may be within range */
> if (test_24bit_addr(ip, addr)) {
> /* within range */
> - if (patch_branch((unsigned int *)ip, addr, BRANCH_SET_LINK)) {
> + if (patch_branch((ppc_inst *)ip, addr, BRANCH_SET_LINK)) {
> pr_err("REL24 out of range!\n");
> return -EINVAL;
> }
> @@ -776,7 +778,7 @@ __ftrace_modify_call(struct dyn_ftrace *rec, unsigned
> long old_addr,
> }
>
> /* Ensure branch is within 24 bits */
> - if (!create_branch((unsigned int *)ip, tramp, BRANCH_SET_LINK)) {
> + if (!create_branch((ppc_inst *)ip, tramp, BRANCH_SET_LINK)) {
> pr_err("Branch out of range\n");
> return -EINVAL;
> }
> @@ -794,7 +796,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned
> long old_addr,
> unsigned long addr)
> {
> unsigned long ip = rec->ip;
> - unsigned int old, new;
> + ppc_inst old, new;
>
> /*
> * If the calling address is more that 24 bits away,
> @@ -834,7 +836,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned
> long old_addr,
> int ftrace_update_ftrace_func(ftrace_func_t func)
> {
> unsigned long ip = (unsigned long)(&ftrace_call);
> - unsigned int old, new;
> + ppc_inst old, new;
> int ret;
>
> old = *(unsigned int *)&ftrace_call;
> @@ -919,7 +921,7 @@ int ftrace_enable_ftrace_graph_caller(void)
> unsigned long ip = (unsigned long)(&ftrace_graph_call);
> unsigned long addr = (unsigned long)(&ftrace_graph_caller);
> unsigned long stub = (unsigned long)(&ftrace_graph_stub);
> - unsigned int old, new;
> + ppc_inst old, new;
>
> old = ftrace_call_replace(ip, stub, 0);
> new = ftrace_call_replace(ip, addr, 0);
> @@ -932,7 +934,7 @@ int ftrace_disable_ftrace_graph_caller(void)
> unsigned long ip = (unsigned long)(&ftrace_graph_call);
> unsigned long addr = (unsigned long)(&ftrace_graph_caller);
> unsigned long stub = (unsigned long)(&ftrace_graph_stub);
> - unsigned int old, new;
> + ppc_inst old, new;
>
> old = ftrace_call_replace(ip, addr, 0);
> new = ftrace_call_replace(ip, stub, 0);
> diff --git a/arch/powerpc/kvm/emulate_loadstore.c
> b/arch/powerpc/kvm/emulate_loadstore.c
> index 1139bc56e004..1c9bcbfeb924 100644
> --- a/arch/powerpc/kvm/emulate_loadstore.c
> +++ b/arch/powerpc/kvm/emulate_loadstore.c
> @@ -21,6 +21,7 @@
> #include <asm/disassemble.h>
> #include <asm/ppc-opcode.h>
> #include <asm/sstep.h>
> +#include <asm/inst.h>
> #include "timing.h"
> #include "trace.h"
>
> diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-
> patching.c
> index 3345f039a876..8492b9e2b8db 100644
> --- a/arch/powerpc/lib/code-patching.c
> +++ b/arch/powerpc/lib/code-patching.c
> @@ -17,9 +17,10 @@
> #include <asm/page.h>
> #include <asm/code-patching.h>
> #include <asm/setup.h>
> +#include <asm/inst.h>
>
> -static int __patch_instruction(unsigned int *exec_addr, unsigned int instr,
> - unsigned int *patch_addr)
> +static int __patch_instruction(ppc_inst *exec_addr, ppc_inst instr,
> + ppc_inst *patch_addr)
> {
> int err = 0;
>
> @@ -33,7 +34,7 @@ static int __patch_instruction(unsigned int *exec_addr,
> unsigned int instr,
> return 0;
> }
>
> -int raw_patch_instruction(unsigned int *addr, unsigned int instr)
> +int raw_patch_instruction(ppc_inst *addr, ppc_inst instr)
> {
> return __patch_instruction(addr, instr, addr);
> }
> @@ -136,10 +137,10 @@ static inline int unmap_patch_area(unsigned long addr)
> return 0;
> }
>
> -static int do_patch_instruction(unsigned int *addr, unsigned int instr)
> +static int do_patch_instruction(ppc_inst *addr, ppc_inst instr)
> {
> int err;
> - unsigned int *patch_addr = NULL;
> + ppc_inst *patch_addr = NULL;
> unsigned long flags;
> unsigned long text_poke_addr;
> unsigned long kaddr = (unsigned long)addr;
> @@ -176,7 +177,7 @@ static int do_patch_instruction(unsigned int *addr,
> unsigned int instr)
> }
> #else /* !CONFIG_STRICT_KERNEL_RWX */
>
> -static int do_patch_instruction(unsigned int *addr, unsigned int instr)
> +static int do_patch_instruction(ppc_inst *addr, ppc_inst instr)
> {
> return raw_patch_instruction(addr, instr);
> }
> @@ -194,7 +195,7 @@ int patch_instruction(unsigned int *addr, unsigned int
> instr)
> }
> NOKPROBE_SYMBOL(patch_instruction);
>
> -int patch_branch(unsigned int *addr, unsigned long target, int flags)
> +int patch_branch(ppc_inst *addr, unsigned long target, int flags)
> {
> return patch_instruction(addr, create_branch(addr, target, flags));
> }
> @@ -225,7 +226,7 @@ bool is_offset_in_branch_range(long offset)
> * Helper to check if a given instruction is a conditional branch
> * Derived from the conditional checks in analyse_instr()
> */
> -bool is_conditional_branch(unsigned int instr)
> +bool is_conditional_branch(ppc_inst instr)
> {
> unsigned int opcode = instr >> 26;
>
> @@ -243,10 +244,10 @@ bool is_conditional_branch(unsigned int instr)
> }
> NOKPROBE_SYMBOL(is_conditional_branch);
>
> -unsigned int create_branch(const unsigned int *addr,
> +ppc_inst create_branch(const ppc_inst *addr,
> unsigned long target, int flags)
> {
> - unsigned int instruction;
> + ppc_inst instruction;
> long offset;
>
> offset = target;
> @@ -266,7 +267,7 @@ unsigned int create_branch(const unsigned int *addr,
> unsigned int create_cond_branch(const unsigned int *addr,
> unsigned long target, int flags)
> {
> - unsigned int instruction;
> + ppc_inst instruction;
> long offset;
>
> offset = target;
> @@ -283,22 +284,22 @@ unsigned int create_cond_branch(const unsigned int
> *addr,
> return instruction;
> }
>
> -static unsigned int branch_opcode(unsigned int instr)
> +static unsigned int branch_opcode(ppc_inst instr)
> {
> return (instr >> 26) & 0x3F;
> }
>
> -static int instr_is_branch_iform(unsigned int instr)
> +static int instr_is_branch_iform(ppc_inst instr)
> {
> return branch_opcode(instr) == 18;
> }
>
> -static int instr_is_branch_bform(unsigned int instr)
> +static int instr_is_branch_bform(ppc_inst instr)
> {
> return branch_opcode(instr) == 16;
> }
>
> -int instr_is_relative_branch(unsigned int instr)
> +int instr_is_relative_branch(ppc_inst instr)
> {
> if (instr & BRANCH_ABSOLUTE)
> return 0;
> @@ -306,12 +307,12 @@ int instr_is_relative_branch(unsigned int instr)
> return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
> }
>
> -int instr_is_relative_link_branch(unsigned int instr)
> +int instr_is_relative_link_branch(ppc_inst instr)
> {
> return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK);
> }
>
> -static unsigned long branch_iform_target(const unsigned int *instr)
> +static unsigned long branch_iform_target(const ppc_inst *instr)
> {
> signed long imm;
>
> @@ -327,7 +328,7 @@ static unsigned long branch_iform_target(const unsigned
> int *instr)
> return (unsigned long)imm;
> }
>
> -static unsigned long branch_bform_target(const unsigned int *instr)
> +static unsigned long branch_bform_target(const ppc_inst *instr)
> {
> signed long imm;
>
> @@ -343,7 +344,7 @@ static unsigned long branch_bform_target(const unsigned
> int *instr)
> return (unsigned long)imm;
> }
>
> -unsigned long branch_target(const unsigned int *instr)
> +unsigned long branch_target(const ppc_inst *instr)
> {
> if (instr_is_branch_iform(*instr))
> return branch_iform_target(instr);
> @@ -353,7 +354,7 @@ unsigned long branch_target(const unsigned int *instr)
> return 0;
> }
>
> -int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr)
> +int instr_is_branch_to_addr(const ppc_inst *instr, unsigned long addr)
> {
> if (instr_is_branch_iform(*instr) || instr_is_branch_bform(*instr))
> return branch_target(instr) == addr;
> @@ -361,7 +362,7 @@ int instr_is_branch_to_addr(const unsigned int *instr,
> unsigned long addr)
> return 0;
> }
>
> -unsigned int translate_branch(const unsigned int *dest, const unsigned int
> *src)
> +ppc_inst translate_branch(const ppc_inst *dest, const ppc_inst *src)
> {
> unsigned long target;
>
> @@ -403,7 +404,7 @@ static void __init test_trampoline(void)
>
> static void __init test_branch_iform(void)
> {
> - unsigned int instr;
> + ppc_inst instr;
> unsigned long addr;
>
> addr = (unsigned long)&instr;
> @@ -478,11 +479,11 @@ static void __init test_branch_iform(void)
>
> static void __init test_create_function_call(void)
> {
> - unsigned int *iptr;
> + ppc_inst *iptr;
> unsigned long dest;
>
> /* Check we can create a function call */
> - iptr = (unsigned int *)ppc_function_entry(test_trampoline);
> + iptr = (ppc_inst *)ppc_function_entry(test_trampoline);
> dest = ppc_function_entry(test_create_function_call);
> patch_instruction(iptr, create_branch(iptr, dest, BRANCH_SET_LINK));
> check(instr_is_branch_to_addr(iptr, dest));
> @@ -491,7 +492,8 @@ static void __init test_create_function_call(void)
> static void __init test_branch_bform(void)
> {
> unsigned long addr;
> - unsigned int *iptr, instr, flags;
> + ppc_inst *iptr, instr;
> + unsigned int flags;
>
> iptr = &instr;
> addr = (unsigned long)iptr;
> @@ -561,7 +563,7 @@ static void __init test_branch_bform(void)
> static void __init test_translate_branch(void)
> {
> unsigned long addr;
> - unsigned int *p, *q;
> + ppc_inst *p, *q;
> void *buf;
>
> buf = vmalloc(PAGE_ALIGN(0x2000000 + 1));
> diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
> index c077acb983a1..1d9c766a89fe 100644
> --- a/arch/powerpc/lib/sstep.c
> +++ b/arch/powerpc/lib/sstep.c
> @@ -1163,7 +1163,7 @@ static nokprobe_inline int trap_compare(long v1, long
> v2)
> * otherwise.
> */
> int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
> - unsigned int instr)
> + ppc_inst instr)
> {
> unsigned int opcode, ra, rb, rc, rd, spr, u;
> unsigned long int imm;
> @@ -3101,7 +3101,7 @@ NOKPROBE_SYMBOL(emulate_loadstore);
> * or -1 if the instruction is one that should not be stepped,
> * such as an rfid, or a mtmsrd that would clear MSR_RI.
> */
> -int emulate_step(struct pt_regs *regs, unsigned int instr)
> +int emulate_step(struct pt_regs *regs, ppc_inst instr)
> {
> struct instruction_op op;
> int r, err, type;
> diff --git a/arch/powerpc/lib/test_emulate_step.c
> b/arch/powerpc/lib/test_emulate_step.c
> index 42347067739c..158efc8a0f53 100644
> --- a/arch/powerpc/lib/test_emulate_step.c
> +++ b/arch/powerpc/lib/test_emulate_step.c
> @@ -460,7 +460,7 @@ struct compute_test {
> struct {
> char *descr;
> unsigned long flags;
> - unsigned int instr;
> + ppc_inst instr;
> struct pt_regs regs;
> } subtests[MAX_SUBTESTS + 1];
> };
> @@ -841,7 +841,7 @@ static struct compute_test compute_tests[] = {
> };
>
> static int __init emulate_compute_instr(struct pt_regs *regs,
> - unsigned int instr)
> + ppc_inst instr)
> {
> struct instruction_op op;
>
> @@ -859,7 +859,7 @@ static int __init emulate_compute_instr(struct pt_regs
> *regs,
> }
>
> static int __init execute_compute_instr(struct pt_regs *regs,
> - unsigned int instr)
> + ppc_inst instr)
> {
> extern int exec_instr(struct pt_regs *regs);
> extern s32 patch__exec_instr;
> @@ -890,7 +890,8 @@ static void __init run_tests_compute(void)
> unsigned long flags;
> struct compute_test *test;
> struct pt_regs *regs, exp, got;
> - unsigned int i, j, k, instr;
> + unsigned int i, j, k;
> + ppc_inst instr;
> bool ignore_gpr, ignore_xer, ignore_ccr, passed;
>
> for (i = 0; i < ARRAY_SIZE(compute_tests); i++) {
> diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
> index 7875d1a37770..a0bc442f9557 100644
> --- a/arch/powerpc/xmon/xmon.c
> +++ b/arch/powerpc/xmon/xmon.c
> @@ -892,7 +892,7 @@ static struct bpt *new_breakpoint(unsigned long a)
> static void insert_bpts(void)
> {
> int i;
> - unsigned int instr;
> + ppc_inst instr;
> struct bpt *bp;
>
> bp = bpts;
> @@ -914,7 +914,7 @@ static void insert_bpts(void)
> patch_instruction(bp->instr, instr);
> if (bp->enabled & BP_CIABR)
> continue;
> - if (patch_instruction((unsigned int *)bp->address,
> + if (patch_instruction((ppc_inst *)bp->address,
> bpinstr) != 0) {
> printf("Couldn't write instruction at %lx, "
> "disabling breakpoint there\n", bp->address);
> @@ -943,7 +943,7 @@ static void remove_bpts(void)
> {
> int i;
> struct bpt *bp;
> - unsigned instr;
> + ppc_inst instr;
>
> bp = bpts;
> for (i = 0; i < NBPTS; ++i, ++bp) {
> @@ -952,7 +952,7 @@ static void remove_bpts(void)
> if (mread(bp->address, &instr, 4) == 4
> && instr == bpinstr
> && patch_instruction(
> - (unsigned int *)bp->address, bp->instr[0]) != 0)
> + (ppc_inst *)bp->address, bp->instr[0]) != 0)
> printf("Couldn't remove breakpoint at %lx\n",
> bp->address);
> }
> @@ -1159,7 +1159,7 @@ static int do_step(struct pt_regs *regs)
> */
> static int do_step(struct pt_regs *regs)
> {
> - unsigned int instr;
> + ppc_inst instr;
> int stepped;
>
> force_enable_xmon();
> @@ -1325,7 +1325,7 @@ csum(void)
> */
> static long check_bp_loc(unsigned long addr)
> {
> - unsigned int instr;
> + ppc_inst instr;
>
> addr &= ~3;
> if (!is_kernel_addr(addr)) {
^ permalink raw reply
* Re: [PATCH v2 0/2] powerpc: Remove support for ppc405/440 Xilinx platforms
From: Michael Ellerman @ 2020-04-01 10:35 UTC (permalink / raw)
To: Michal Simek, Michal Simek, linux-kernel, monstr, git, sfr, maz
Cc: Kate Stewart, Mark Rutland, Desnes A. Nunes do Rosario,
Geert Uytterhoeven, linux-doc, alsa-devel, dri-devel,
Jaroslav Kysela, Richard Fontana, Paul Mackerras, Miquel Raynal,
Mauro Carvalho Chehab, Fabio Estevam, Sasha Levin,
Jonathan Corbet, Masahiro Yamada, YueHaibing, Krzysztof Kozlowski,
Allison Randal, linux-arm-kernel, devicetree, Andrew Donnellan,
Arnd Bergmann, Bartlomiej Zolnierkiewicz, Alistair Popple,
linuxppc-dev, Nicholas Piggin, Alexios Zavras, Mark Brown,
linux-fbdev, Jonathan Cameron, Thomas Gleixner, Andy Shevchenko,
Dmitry Vyukov, Wei Hu, Greg Kroah-Hartman, Nick Desaulniers,
Takashi Iwai, Rob Herring, Enrico Weigelt, David S. Miller,
Thiago Jung Bauermann
In-Reply-To: <12db51d6-d848-118e-5ec1-a4172bd47aa4@xilinx.com>
Michal Simek <michal.simek@xilinx.com> writes:
> On 01. 04. 20 4:07, Michael Ellerman wrote:
>> Michal Simek <michal.simek@xilinx.com> writes:
>>> Hi,
>>>
>>> recently we wanted to update xilinx intc driver and we found that function
>>> which we wanted to remove is still wired by ancient Xilinx PowerPC
>>> platforms. Here is the thread about it.
>>> https://lore.kernel.org/linux-next/48d3232d-0f1d-42ea-3109-f44bbabfa2e8@xilinx.com/
>>>
>>> I have been talking about it internally and there is no interest in these
>>> platforms and it is also orphan for quite a long time. None is really
>>> running/testing these platforms regularly that's why I think it makes sense
>>> to remove them also with drivers which are specific to this platform.
>>>
>>> U-Boot support was removed in 2017 without anybody complain about it
>>> https://github.com/Xilinx/u-boot-xlnx/commit/98f705c9cefdfdba62c069821bbba10273a0a8ed
>>>
>>> Based on current ppc/next.
>>>
>>> If anyone has any objection about it, please let me know.
>>
>> Thanks for taking the time to find all this code and remove it.
>>
>> I'm not going to take this series for v5.7, it was posted too close to
>> the merge window, and doing so wouldn't give people much time to object,
>> especially given people are distracted at the moment.
>>
>> I'm happy to take it for v5.8, assuming there's no major objections.
>
> Sure. Just to let you know Christophe Leroy included this patch in his
> series about ppc405 removal. It should be the same.
>
> If you don't want to take that alsa patch I can send it separately and
> this patch can be taken from his series. I don't really mind but please
> let me know what way you prefer.
It's better to keep it all together, so I'm happy take the alsa patch as
well, it's already been acked.
cheers
^ permalink raw reply
* Re: [PATCH v2 0/2] powerpc: Remove support for ppc405/440 Xilinx platforms
From: Takashi Iwai @ 2020-04-01 10:38 UTC (permalink / raw)
To: Michael Ellerman
Cc: Kate Stewart, Mark Rutland, Desnes A. Nunes do Rosario,
Geert Uytterhoeven, linux-doc, alsa-devel, dri-devel,
Jaroslav Kysela, Richard Fontana, Paul Mackerras, Miquel Raynal,
Mauro Carvalho Chehab, Fabio Estevam, Sasha Levin, sfr,
Jonathan Corbet, maz, Masahiro Yamada, Takashi Iwai, YueHaibing,
Michal Simek, Krzysztof Kozlowski, Allison Randal,
linux-arm-kernel, devicetree, Andrew Donnellan, Arnd Bergmann,
Bartlomiej Zolnierkiewicz, Alistair Popple, linuxppc-dev,
Nicholas Piggin, Alexios Zavras, Mark Brown, git, linux-fbdev,
Jonathan Cameron, Thomas Gleixner, Andy Shevchenko, Dmitry Vyukov,
monstr, Wei Hu, Greg Kroah-Hartman, Nick Desaulniers,
linux-kernel, Rob Herring, Enrico Weigelt, David S. Miller,
Thiago Jung Bauermann
In-Reply-To: <87y2rftrx7.fsf@mpe.ellerman.id.au>
On Wed, 01 Apr 2020 12:35:16 +0200,
Michael Ellerman wrote:
>
> Michal Simek <michal.simek@xilinx.com> writes:
> > On 01. 04. 20 4:07, Michael Ellerman wrote:
> >> Michal Simek <michal.simek@xilinx.com> writes:
> >>> Hi,
> >>>
> >>> recently we wanted to update xilinx intc driver and we found that function
> >>> which we wanted to remove is still wired by ancient Xilinx PowerPC
> >>> platforms. Here is the thread about it.
> >>> https://lore.kernel.org/linux-next/48d3232d-0f1d-42ea-3109-f44bbabfa2e8@xilinx.com/
> >>>
> >>> I have been talking about it internally and there is no interest in these
> >>> platforms and it is also orphan for quite a long time. None is really
> >>> running/testing these platforms regularly that's why I think it makes sense
> >>> to remove them also with drivers which are specific to this platform.
> >>>
> >>> U-Boot support was removed in 2017 without anybody complain about it
> >>> https://github.com/Xilinx/u-boot-xlnx/commit/98f705c9cefdfdba62c069821bbba10273a0a8ed
> >>>
> >>> Based on current ppc/next.
> >>>
> >>> If anyone has any objection about it, please let me know.
> >>
> >> Thanks for taking the time to find all this code and remove it.
> >>
> >> I'm not going to take this series for v5.7, it was posted too close to
> >> the merge window, and doing so wouldn't give people much time to object,
> >> especially given people are distracted at the moment.
> >>
> >> I'm happy to take it for v5.8, assuming there's no major objections.
> >
> > Sure. Just to let you know Christophe Leroy included this patch in his
> > series about ppc405 removal. It should be the same.
> >
> > If you don't want to take that alsa patch I can send it separately and
> > this patch can be taken from his series. I don't really mind but please
> > let me know what way you prefer.
>
> It's better to keep it all together, so I'm happy take the alsa patch as
> well, it's already been acked.
Sure, please go ahead.
thanks,
Takashi
^ permalink raw reply
* Re: [PATCH v3 3/3] powerpc test_emulate_step: add testcases for divde[.] and divdeu[.] instructions
From: Naveen N. Rao @ 2020-04-01 10:56 UTC (permalink / raw)
To: Balamuruhan S, mpe; +Cc: paulus, linuxppc-dev, sandipan, ravi.bangoria
In-Reply-To: <20200120065426.3259-4-bala24@linux.ibm.com>
Balamuruhan S wrote:
> add testcases for divde, divde., divdeu, divdeu. emulated
> instructions to cover few scenarios,
> * with same dividend and divisor to have undefine RT
> for divdeu[.]
> * with divide by zero to have undefine RT for both
> divde[.] and divdeu[.]
> * with negative dividend to cover -|divisor| < r <= 0 if
> the dividend is negative for divde[.]
> * normal case with proper dividend and divisor for both
> divde[.] and divdeu[.]
>
> Reviewed-by: Sandipan Das <sandipan@linux.ibm.com>
> Signed-off-by: Balamuruhan S <bala24@linux.ibm.com>
> ---
> arch/powerpc/lib/test_emulate_step.c | 164 +++++++++++++++++++++++++++++++++++
> 1 file changed, 164 insertions(+)
>
> diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c
> index 42347067739c..ffeb9b68a31d 100644
> --- a/arch/powerpc/lib/test_emulate_step.c
> +++ b/arch/powerpc/lib/test_emulate_step.c
> @@ -53,6 +53,14 @@
> ___PPC_RA(a) | ___PPC_RB(b))
> #define TEST_ADDC_DOT(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | \
> ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
> +#define TEST_DIVDE(t, a, b) (PPC_INST_DIVDE | ___PPC_RT(t) | \
> + ___PPC_RA(a) | ___PPC_RB(b))
> +#define TEST_DIVDE_DOT(t, a, b) (PPC_INST_DIVDE | ___PPC_RT(t) | \
> + ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
> +#define TEST_DIVDEU(t, a, b) (PPC_INST_DIVDEU | ___PPC_RT(t) | \
> + ___PPC_RA(a) | ___PPC_RB(b))
> +#define TEST_DIVDEU_DOT(t, a, b)(PPC_INST_DIVDEU | ___PPC_RT(t) | \
> + ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
>
> #define MAX_SUBTESTS 16
>
> @@ -837,6 +845,162 @@ static struct compute_test compute_tests[] = {
> }
> }
> }
> + },
> + {
> + .mnemonic = "divde",
> + .subtests = {
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> + .instr = TEST_DIVDE(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MIN,
> + }
> + },
> + {
> + .descr = "RA = 1L, RB = 0",
> + .instr = TEST_DIVDE(20, 21, 22),
> + .flags = IGNORE_GPR(20),
> + .regs = {
> + .gpr[21] = 1L,
> + .gpr[22] = 0,
> + }
> + },
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> + .instr = TEST_DIVDE(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MAX,
> + }
> + }
> + }
> + },
> + {
> + .mnemonic = "divde.",
> + .subtests = {
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> + .instr = TEST_DIVDE_DOT(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MIN,
> + }
> + },
> + {
> + .descr = "RA = 1L, RB = 0",
> + .instr = TEST_DIVDE_DOT(20, 21, 22),
> + .flags = IGNORE_GPR(20),
> + .regs = {
> + .gpr[21] = 1L,
> + .gpr[22] = 0,
> + }
> + },
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> + .instr = TEST_DIVDE_DOT(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MAX,
> + }
> + }
> + }
> + },
> + {
> + .mnemonic = "divdeu",
> + .subtests = {
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> + .instr = TEST_DIVDEU(20, 21, 22),
> + .flags = IGNORE_GPR(20),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MIN,
> + }
> + },
> + {
> + .descr = "RA = 1L, RB = 0",
> + .instr = TEST_DIVDEU(20, 21, 22),
> + .flags = IGNORE_GPR(20),
> + .regs = {
> + .gpr[21] = 1L,
> + .gpr[22] = 0,
> + }
> + },
> + {
> + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> + .instr = TEST_DIVDEU(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MIN,
> + .gpr[22] = LONG_MAX,
> + }
> + },
> + {
> + .descr = "RA = LONG_MAX - 1, RB = LONG_MAX",
> + .instr = TEST_DIVDEU_DOT(20, 21, 22),
> + .regs = {
> + .gpr[21] = LONG_MAX - 1,
> + .gpr[22] = LONG_MAX,
> + }
> + },
> + {
> + .descr = "RA = LONG_MIN + 1, RB = LONG_MIN",
> + .instr = TEST_DIVDEU_DOT(20, 21, 22),
I think you meant to use TEST_DIVDEU here ^^^^^ and in the test before,
rather than TEST_DIVDEU_DOT.
Apart from them, for this series:
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
- Naveen
^ permalink raw reply
* Re: [PATCH v3 3/3] powerpc test_emulate_step: add testcases for divde[.] and divdeu[.] instructions
From: Balamuruhan S @ 2020-04-01 11:21 UTC (permalink / raw)
To: Naveen N. Rao, mpe; +Cc: paulus, linuxppc-dev, sandipan, ravi.bangoria
In-Reply-To: <1585738460.fzpd1gjpyj.naveen@linux.ibm.com>
On Wed, 2020-04-01 at 16:26 +0530, Naveen N. Rao wrote:
> Balamuruhan S wrote:
> > add testcases for divde, divde., divdeu, divdeu. emulated
> > instructions to cover few scenarios,
> > * with same dividend and divisor to have undefine RT
> > for divdeu[.]
> > * with divide by zero to have undefine RT for both
> > divde[.] and divdeu[.]
> > * with negative dividend to cover -|divisor| < r <= 0 if
> > the dividend is negative for divde[.]
> > * normal case with proper dividend and divisor for both
> > divde[.] and divdeu[.]
> >
> > Reviewed-by: Sandipan Das <sandipan@linux.ibm.com>
> > Signed-off-by: Balamuruhan S <bala24@linux.ibm.com>
> > ---
> > arch/powerpc/lib/test_emulate_step.c | 164
> > +++++++++++++++++++++++++++++++++++
> > 1 file changed, 164 insertions(+)
> >
> > diff --git a/arch/powerpc/lib/test_emulate_step.c
> > b/arch/powerpc/lib/test_emulate_step.c
> > index 42347067739c..ffeb9b68a31d 100644
> > --- a/arch/powerpc/lib/test_emulate_step.c
> > +++ b/arch/powerpc/lib/test_emulate_step.c
> > @@ -53,6 +53,14 @@
> > ___PPC_RA(a) | ___PPC_RB(b))
> > #define TEST_ADDC_DOT(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) |
> > \
> > ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
> > +#define TEST_DIVDE(t, a, b) (PPC_INST_DIVDE | ___PPC_RT(t) | \
> > + ___PPC_RA(a) | ___PPC_RB(b))
> > +#define TEST_DIVDE_DOT(t, a, b) (PPC_INST_DIVDE | ___PPC_RT(t) | \
> > + ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
> > +#define TEST_DIVDEU(t, a, b) (PPC_INST_DIVDEU | ___PPC_RT(t) | \
> > + ___PPC_RA(a) | ___PPC_RB(b))
> > +#define TEST_DIVDEU_DOT(t, a, b)(PPC_INST_DIVDEU | ___PPC_RT(t) | \
> > + ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
> >
> > #define MAX_SUBTESTS 16
> >
> > @@ -837,6 +845,162 @@ static struct compute_test compute_tests[] = {
> > }
> > }
> > }
> > + },
> > + {
> > + .mnemonic = "divde",
> > + .subtests = {
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> > + .instr = TEST_DIVDE(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MIN,
> > + }
> > + },
> > + {
> > + .descr = "RA = 1L, RB = 0",
> > + .instr = TEST_DIVDE(20, 21, 22),
> > + .flags = IGNORE_GPR(20),
> > + .regs = {
> > + .gpr[21] = 1L,
> > + .gpr[22] = 0,
> > + }
> > + },
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> > + .instr = TEST_DIVDE(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MAX,
> > + }
> > + }
> > + }
> > + },
> > + {
> > + .mnemonic = "divde.",
> > + .subtests = {
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> > + .instr = TEST_DIVDE_DOT(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MIN,
> > + }
> > + },
> > + {
> > + .descr = "RA = 1L, RB = 0",
> > + .instr = TEST_DIVDE_DOT(20, 21, 22),
> > + .flags = IGNORE_GPR(20),
> > + .regs = {
> > + .gpr[21] = 1L,
> > + .gpr[22] = 0,
> > + }
> > + },
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> > + .instr = TEST_DIVDE_DOT(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MAX,
> > + }
> > + }
> > + }
> > + },
> > + {
> > + .mnemonic = "divdeu",
> > + .subtests = {
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MIN",
> > + .instr = TEST_DIVDEU(20, 21, 22),
> > + .flags = IGNORE_GPR(20),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MIN,
> > + }
> > + },
> > + {
> > + .descr = "RA = 1L, RB = 0",
> > + .instr = TEST_DIVDEU(20, 21, 22),
> > + .flags = IGNORE_GPR(20),
> > + .regs = {
> > + .gpr[21] = 1L,
> > + .gpr[22] = 0,
> > + }
> > + },
> > + {
> > + .descr = "RA = LONG_MIN, RB = LONG_MAX",
> > + .instr = TEST_DIVDEU(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MIN,
> > + .gpr[22] = LONG_MAX,
> > + }
> > + },
> > + {
> > + .descr = "RA = LONG_MAX - 1, RB = LONG_MAX",
> > + .instr = TEST_DIVDEU_DOT(20, 21, 22),
> > + .regs = {
> > + .gpr[21] = LONG_MAX - 1,
> > + .gpr[22] = LONG_MAX,
> > + }
> > + },
> > + {
> > + .descr = "RA = LONG_MIN + 1, RB = LONG_MIN",
> > + .instr = TEST_DIVDEU_DOT(20, 21, 22),
>
> I think you meant to use TEST_DIVDEU here ^^^^^ and in the test before,
> rather than TEST_DIVDEU_DOT.
yes indeed, I will fix it in my next version.
>
> Apart from them, for this series:
> Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Thank you Naveen for reviewing.
-- Bala
>
>
> - Naveen
>
^ permalink raw reply
* Re: [PATCH v2 0/2] powerpc: Remove support for ppc405/440 Xilinx platforms
From: Michal Simek @ 2020-04-01 11:30 UTC (permalink / raw)
To: Takashi Iwai, Michael Ellerman
Cc: Kate Stewart, Mark Rutland, Desnes A. Nunes do Rosario,
Geert Uytterhoeven, linux-doc, alsa-devel, dri-devel,
Jaroslav Kysela, Richard Fontana, Paul Mackerras, Miquel Raynal,
Mauro Carvalho Chehab, Fabio Estevam, Sasha Levin, sfr,
Jonathan Corbet, maz, Masahiro Yamada, Takashi Iwai, YueHaibing,
Michal Simek, Krzysztof Kozlowski, Allison Randal,
linux-arm-kernel, devicetree, Andrew Donnellan, Arnd Bergmann,
Bartlomiej Zolnierkiewicz, Alistair Popple, linuxppc-dev,
Nicholas Piggin, Alexios Zavras, Mark Brown, git, linux-fbdev,
Jonathan Cameron, Thomas Gleixner, Andy Shevchenko, Dmitry Vyukov,
monstr, Wei Hu, Greg Kroah-Hartman, Nick Desaulniers,
linux-kernel, Rob Herring, Enrico Weigelt, David S. Miller,
Thiago Jung Bauermann
In-Reply-To: <s5hk12z4hj5.wl-tiwai@suse.de>
On 01. 04. 20 12:38, Takashi Iwai wrote:
> On Wed, 01 Apr 2020 12:35:16 +0200,
> Michael Ellerman wrote:
>>
>> Michal Simek <michal.simek@xilinx.com> writes:
>>> On 01. 04. 20 4:07, Michael Ellerman wrote:
>>>> Michal Simek <michal.simek@xilinx.com> writes:
>>>>> Hi,
>>>>>
>>>>> recently we wanted to update xilinx intc driver and we found that function
>>>>> which we wanted to remove is still wired by ancient Xilinx PowerPC
>>>>> platforms. Here is the thread about it.
>>>>> https://lore.kernel.org/linux-next/48d3232d-0f1d-42ea-3109-f44bbabfa2e8@xilinx.com/
>>>>>
>>>>> I have been talking about it internally and there is no interest in these
>>>>> platforms and it is also orphan for quite a long time. None is really
>>>>> running/testing these platforms regularly that's why I think it makes sense
>>>>> to remove them also with drivers which are specific to this platform.
>>>>>
>>>>> U-Boot support was removed in 2017 without anybody complain about it
>>>>> https://github.com/Xilinx/u-boot-xlnx/commit/98f705c9cefdfdba62c069821bbba10273a0a8ed
>>>>>
>>>>> Based on current ppc/next.
>>>>>
>>>>> If anyone has any objection about it, please let me know.
>>>>
>>>> Thanks for taking the time to find all this code and remove it.
>>>>
>>>> I'm not going to take this series for v5.7, it was posted too close to
>>>> the merge window, and doing so wouldn't give people much time to object,
>>>> especially given people are distracted at the moment.
>>>>
>>>> I'm happy to take it for v5.8, assuming there's no major objections.
>>>
>>> Sure. Just to let you know Christophe Leroy included this patch in his
>>> series about ppc405 removal. It should be the same.
>>>
>>> If you don't want to take that alsa patch I can send it separately and
>>> this patch can be taken from his series. I don't really mind but please
>>> let me know what way you prefer.
>>
>> It's better to keep it all together, so I'm happy take the alsa patch as
>> well, it's already been acked.
>
> Sure, please go ahead.
g8.
Thanks,
Michal
^ permalink raw reply
* Re: [RFC WIP PATCH] powerpc/32: system call implement entry/exit logic in C
From: Christophe Leroy @ 2020-04-01 11:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, npiggin,
msuchanek
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <bbc0a09cfaf523bc00893253a7101362c98b31eb.1585667934.git.christophe.leroy@c-s.fr>
Le 31/03/2020 à 17:22, Christophe Leroy a écrit :
> That's first try to port PPC64 syscall entry/exit logic in C to PPC32.
> I've do the minimum to get it work. I have not reworked calls
> to sys_fork() and friends for instance.
>
> For the time being, it seems to work more or less but:
> - ping reports EINVAL on recvfrom
> - strace shows NULL instead of strings in call like open() for instance.
For the two above problems, that's because system_call_exception()
doesn't set orig_gpr3 whereas DoSycall() does in entry_32.S . Is that
only done on PPC32 ?
With the following line at the begining of system_call_exception(), it
works perfectly:
regs->orig_gpr3 = r3;
I will now focus on performance to see if we can do something about it.
Christophe
> - the performance is definitively bad
>
> On an 8xx, null_syscall test is about 30% slower after this patch:
> - Without the patch: 284 cycles
> - With the patch: 371 cycles
>
> @nick and others, any suggestion to fix and improve ?
>
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> ---
> arch/powerpc/include/asm/book3s/32/kup.h | 21 ++
> .../powerpc/include/asm/book3s/64/kup-radix.h | 12 +-
> arch/powerpc/include/asm/hw_irq.h | 15 +
> arch/powerpc/include/asm/kup.h | 2 +
> arch/powerpc/include/asm/nohash/32/kup-8xx.h | 13 +
> arch/powerpc/kernel/Makefile | 5 +-
> arch/powerpc/kernel/entry_32.S | 259 ++----------------
> arch/powerpc/kernel/head_32.h | 3 +-
> .../kernel/{syscall_64.c => syscall.c} | 25 +-
> 9 files changed, 102 insertions(+), 253 deletions(-)
> rename arch/powerpc/kernel/{syscall_64.c => syscall.c} (97%)
>
> diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h
> index 3c0ba22dc360..c85bc5b56366 100644
> --- a/arch/powerpc/include/asm/book3s/32/kup.h
> +++ b/arch/powerpc/include/asm/book3s/32/kup.h
> @@ -102,6 +102,27 @@ static inline void kuap_update_sr(u32 sr, u32 addr, u32 end)
> isync(); /* Context sync required after mtsrin() */
> }
>
> +static inline void kuap_restore(struct pt_regs *regs)
> +{
> + u32 kuap = current->thread.kuap;
> + u32 addr = kuap & 0xf0000000;
> + u32 end = kuap << 28;
> +
> + if (unlikely(!kuap))
> + return;
> +
> + current->thread.kuap = 0;
> + kuap_update_sr(mfsrin(addr) & ~SR_KS, addr, end); /* Clear Ks */
> +}
> +
> +static inline void kuap_check(void)
> +{
> + if (!IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
> + return;
> +
> + WARN_ON_ONCE(current->thread.kuap != 0);
> +}
> +
> static __always_inline void allow_user_access(void __user *to, const void __user *from,
> u32 size, unsigned long dir)
> {
> diff --git a/arch/powerpc/include/asm/book3s/64/kup-radix.h b/arch/powerpc/include/asm/book3s/64/kup-radix.h
> index 3bcef989a35d..1f2716a0dcd8 100644
> --- a/arch/powerpc/include/asm/book3s/64/kup-radix.h
> +++ b/arch/powerpc/include/asm/book3s/64/kup-radix.h
> @@ -60,13 +60,13 @@
> #include <asm/mmu.h>
> #include <asm/ptrace.h>
>
> -static inline void kuap_restore_amr(struct pt_regs *regs)
> +static inline void kuap_restore(struct pt_regs *regs)
> {
> if (mmu_has_feature(MMU_FTR_RADIX_KUAP))
> mtspr(SPRN_AMR, regs->kuap);
> }
>
> -static inline void kuap_check_amr(void)
> +static inline void kuap_check(void)
> {
> if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && mmu_has_feature(MMU_FTR_RADIX_KUAP))
> WARN_ON_ONCE(mfspr(SPRN_AMR) != AMR_KUAP_BLOCKED);
> @@ -141,14 +141,6 @@ bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
> (regs->kuap & (is_write ? AMR_KUAP_BLOCK_WRITE : AMR_KUAP_BLOCK_READ)),
> "Bug: %s fault blocked by AMR!", is_write ? "Write" : "Read");
> }
> -#else /* CONFIG_PPC_KUAP */
> -static inline void kuap_restore_amr(struct pt_regs *regs)
> -{
> -}
> -
> -static inline void kuap_check_amr(void)
> -{
> -}
> #endif /* CONFIG_PPC_KUAP */
>
> #endif /* __ASSEMBLY__ */
> diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
> index e0e71777961f..6ccf07de6665 100644
> --- a/arch/powerpc/include/asm/hw_irq.h
> +++ b/arch/powerpc/include/asm/hw_irq.h
> @@ -321,6 +321,16 @@ static inline void arch_local_irq_disable(void)
> mtmsr(mfmsr() & ~MSR_EE);
> }
>
> +static inline void arch_local_recovery_disable(void)
> +{
> + if (IS_ENABLED(CONFIG_BOOKE))
> + wrtee(0);
> + else if (IS_ENABLED(CONFIG_PPC_8xx))
> + wrtspr(SPRN_NRI);
> + else
> + mtmsr(mfmsr() & ~(MSR_EE | MSR_RI));
> +}
> +
> static inline void arch_local_irq_enable(void)
> {
> if (IS_ENABLED(CONFIG_BOOKE))
> @@ -343,6 +353,11 @@ static inline bool arch_irqs_disabled(void)
>
> #define hard_irq_disable() arch_local_irq_disable()
>
> +#define __hard_irq_enable() arch_local_irq_enable()
> +#define __hard_irq_disable() arch_local_irq_disable()
> +#define __hard_EE_RI_disable() arch_local_recovery_disable()
> +#define __hard_RI_enable() arch_local_irq_disable()
> +
> static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
> {
> return !(regs->msr & MSR_EE);
> diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h
> index 92bcd1a26d73..1100c13b6d9e 100644
> --- a/arch/powerpc/include/asm/kup.h
> +++ b/arch/powerpc/include/asm/kup.h
> @@ -62,6 +62,8 @@ bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
> {
> return false;
> }
> +static inline void kuap_restore(struct pt_regs *regs) { }
> +static inline void kuap_check(void) { }
> #endif /* CONFIG_PPC_KUAP */
>
> static inline void allow_read_from_user(const void __user *from, unsigned long size)
> diff --git a/arch/powerpc/include/asm/nohash/32/kup-8xx.h b/arch/powerpc/include/asm/nohash/32/kup-8xx.h
> index 85ed2390fb99..1918d2e55da3 100644
> --- a/arch/powerpc/include/asm/nohash/32/kup-8xx.h
> +++ b/arch/powerpc/include/asm/nohash/32/kup-8xx.h
> @@ -34,6 +34,19 @@
>
> #include <asm/reg.h>
>
> +static inline void kuap_restore(struct pt_regs *regs)
> +{
> + mtspr(SPRN_MD_AP, regs->kuap);
> +}
> +
> +static inline void kuap_check(void)
> +{
> + if (!IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
> + return;
> +
> + WARN_ON_ONCE((mfspr(SPRN_MD_AP) & 0xffff0000) != (MD_APG_KUAP & 0xffff0000));
> +}
> +
> static inline void allow_user_access(void __user *to, const void __user *from,
> unsigned long size, unsigned long dir)
> {
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index 570660efbb3d..e4be425b7718 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -45,11 +45,10 @@ obj-y := cputable.o syscalls.o \
> signal.o sysfs.o cacheinfo.o time.o \
> prom.o traps.o setup-common.o \
> udbg.o misc.o io.o misc_$(BITS).o \
> - of_platform.o prom_parse.o
> + of_platform.o prom_parse.o syscall.o
> obj-y += ptrace/
> obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o signal_64.o \
> - paca.o nvram_64.o firmware.o note.o \
> - syscall_64.o
> + paca.o nvram_64.o firmware.o note.o
> obj-$(CONFIG_VDSO32) += vdso32/
> obj-$(CONFIG_PPC_WATCHDOG) += watchdog.o
> obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
> diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
> index a6371fb8f761..103f5158bc44 100644
> --- a/arch/powerpc/kernel/entry_32.S
> +++ b/arch/powerpc/kernel/entry_32.S
> @@ -315,162 +315,37 @@ stack_ovf:
> RFI
> #endif
>
> -#ifdef CONFIG_TRACE_IRQFLAGS
> -trace_syscall_entry_irq_off:
> - /*
> - * Syscall shouldn't happen while interrupts are disabled,
> - * so let's do a warning here.
> - */
> -0: trap
> - EMIT_BUG_ENTRY 0b,__FILE__,__LINE__, BUGFLAG_WARNING
> - bl trace_hardirqs_on
> -
> - /* Now enable for real */
> - LOAD_REG_IMMEDIATE(r10, MSR_KERNEL | MSR_EE)
> - mtmsr r10
> -
> - REST_GPR(0, r1)
> - REST_4GPRS(3, r1)
> - REST_2GPRS(7, r1)
> - b DoSyscall
> -#endif /* CONFIG_TRACE_IRQFLAGS */
> -
> .globl transfer_to_syscall
> transfer_to_syscall:
> -#ifdef CONFIG_TRACE_IRQFLAGS
> - andi. r12,r9,MSR_EE
> - beq- trace_syscall_entry_irq_off
> -#endif /* CONFIG_TRACE_IRQFLAGS */
> -
> -/*
> - * Handle a system call.
> - */
> - .stabs "arch/powerpc/kernel/",N_SO,0,0,0f
> - .stabs "entry_32.S",N_SO,0,0,0f
> -0:
> -
> -_GLOBAL(DoSyscall)
> - stw r3,ORIG_GPR3(r1)
> - li r12,0
> - stw r12,RESULT(r1)
> -#ifdef CONFIG_TRACE_IRQFLAGS
> - /* Make sure interrupts are enabled */
> - mfmsr r11
> - andi. r12,r11,MSR_EE
> - /* We came in with interrupts disabled, we WARN and mark them enabled
> - * for lockdep now */
> -0: tweqi r12, 0
> - EMIT_BUG_ENTRY 0b,__FILE__,__LINE__, BUGFLAG_WARNING
> -#endif /* CONFIG_TRACE_IRQFLAGS */
> - lwz r11,TI_FLAGS(r2)
> - andi. r11,r11,_TIF_SYSCALL_DOTRACE
> - bne- syscall_dotrace
> -syscall_dotrace_cont:
> - cmplwi 0,r0,NR_syscalls
> - lis r10,sys_call_table@h
> - ori r10,r10,sys_call_table@l
> - slwi r0,r0,2
> - bge- 66f
> -
> - barrier_nospec_asm
> - /*
> - * Prevent the load of the handler below (based on the user-passed
> - * system call number) being speculatively executed until the test
> - * against NR_syscalls and branch to .66f above has
> - * committed.
> - */
> -
> - lwzx r10,r10,r0 /* Fetch system call handler [ptr] */
> - mtlr r10
> - addi r9,r1,STACK_FRAME_OVERHEAD
> - PPC440EP_ERR42
> - blrl /* Call handler */
> - .globl ret_from_syscall
> + mr r9, r0
> + addi r10, r1, STACK_FRAME_OVERHEAD
> + bl system_call_exception
> ret_from_syscall:
> -#ifdef CONFIG_DEBUG_RSEQ
> - /* Check whether the syscall is issued inside a restartable sequence */
> - stw r3,GPR3(r1)
> - addi r3,r1,STACK_FRAME_OVERHEAD
> - bl rseq_syscall
> - lwz r3,GPR3(r1)
> -#endif
> - mr r6,r3
> - /* disable interrupts so current_thread_info()->flags can't change */
> - LOAD_REG_IMMEDIATE(r10,MSR_KERNEL) /* doesn't include MSR_EE */
> - /* Note: We don't bother telling lockdep about it */
> - SYNC
> - mtmsr r10
> - lwz r9,TI_FLAGS(r2)
> - li r8,-MAX_ERRNO
> - andi. r0,r9,(_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
> - bne- syscall_exit_work
> - cmplw 0,r3,r8
> - blt+ syscall_exit_cont
> - lwz r11,_CCR(r1) /* Load CR */
> - neg r3,r3
> - oris r11,r11,0x1000 /* Set SO bit in CR */
> - stw r11,_CCR(r1)
> -syscall_exit_cont:
> - lwz r8,_MSR(r1)
> -#ifdef CONFIG_TRACE_IRQFLAGS
> - /* If we are going to return from the syscall with interrupts
> - * off, we trace that here. It shouldn't normally happen.
> - */
> - andi. r10,r8,MSR_EE
> - bne+ 1f
> - stw r3,GPR3(r1)
> - bl trace_hardirqs_off
> - lwz r3,GPR3(r1)
> -1:
> -#endif /* CONFIG_TRACE_IRQFLAGS */
> -#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
> - /* If the process has its own DBCR0 value, load it up. The internal
> - debug mode bit tells us that dbcr0 should be loaded. */
> - lwz r0,THREAD+THREAD_DBCR0(r2)
> - andis. r10,r0,DBCR0_IDM@h
> - bnel- load_dbcr0
> -#endif
> -#ifdef CONFIG_44x
> -BEGIN_MMU_FTR_SECTION
> - lis r4,icache_44x_need_flush@ha
> - lwz r5,icache_44x_need_flush@l(r4)
> - cmplwi cr0,r5,0
> - bne- 2f
> -1:
> -END_MMU_FTR_SECTION_IFCLR(MMU_FTR_TYPE_47x)
> -#endif /* CONFIG_44x */
> -BEGIN_FTR_SECTION
> - lwarx r7,0,r1
> -END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX)
> - stwcx. r0,0,r1 /* to clear the reservation */
> - ACCOUNT_CPU_USER_EXIT(r2, r5, r7)
> -#ifdef CONFIG_PPC_BOOK3S_32
> - kuep_unlock r5, r7
> -#endif
> - kuap_check r2, r4
> - lwz r4,_LINK(r1)
> - lwz r5,_CCR(r1)
> - mtlr r4
> - mtcr r5
> - lwz r7,_NIP(r1)
> - lwz r2,GPR2(r1)
> - lwz r1,GPR1(r1)
> -#if defined(CONFIG_PPC_8xx) && defined(CONFIG_PERF_EVENTS)
> - mtspr SPRN_NRI, r0
> -#endif
> - mtspr SPRN_SRR0,r7
> - mtspr SPRN_SRR1,r8
> - SYNC
> - RFI
> -#ifdef CONFIG_44x
> -2: li r7,0
> - iccci r0,r0
> - stw r7,icache_44x_need_flush@l(r4)
> + addi r4, r1, STACK_FRAME_OVERHEAD
> + bl syscall_exit_prepare
> + lwz r2, _CCR(r1)
> + lwz r4, _NIP(r1)
> + lwz r5, _MSR(r1)
> + lwz r6, _LINK(r1)
> + mtspr SPRN_SRR0, r4
> + mtspr SPRN_SRR1, r5
> + mtlr r6
> + cmpwi r3, 0
> + bne 2f
> +1: mtcr r2
> + REST_GPR(2, r1)
> + REST_GPR(3, r1)
> + REST_GPR(1, r1)
> + rfi
> +2: lwz r3, _CTR(r1)
> + lwz r4, _XER(r1)
> + REST_NVGPRS(r1)
> + mtctr r3
> + mtspr SPRN_XER, r4
> + REST_GPR(0, r1)
> + REST_8GPRS(4, r1)
> + REST_GPR(12, r1)
> b 1b
> -#endif /* CONFIG_44x */
> -
> -66: li r3,-ENOSYS
> - b ret_from_syscall
>
> .globl ret_from_fork
> ret_from_fork:
> @@ -490,86 +365,6 @@ ret_from_kernel_thread:
> li r3,0
> b ret_from_syscall
>
> -/* Traced system call support */
> -syscall_dotrace:
> - SAVE_NVGPRS(r1)
> - li r0,0xc00
> - stw r0,_TRAP(r1)
> - addi r3,r1,STACK_FRAME_OVERHEAD
> - bl do_syscall_trace_enter
> - /*
> - * Restore argument registers possibly just changed.
> - * We use the return value of do_syscall_trace_enter
> - * for call number to look up in the table (r0).
> - */
> - mr r0,r3
> - lwz r3,GPR3(r1)
> - lwz r4,GPR4(r1)
> - lwz r5,GPR5(r1)
> - lwz r6,GPR6(r1)
> - lwz r7,GPR7(r1)
> - lwz r8,GPR8(r1)
> - REST_NVGPRS(r1)
> -
> - cmplwi r0,NR_syscalls
> - /* Return code is already in r3 thanks to do_syscall_trace_enter() */
> - bge- ret_from_syscall
> - b syscall_dotrace_cont
> -
> -syscall_exit_work:
> - andi. r0,r9,_TIF_RESTOREALL
> - beq+ 0f
> - REST_NVGPRS(r1)
> - b 2f
> -0: cmplw 0,r3,r8
> - blt+ 1f
> - andi. r0,r9,_TIF_NOERROR
> - bne- 1f
> - lwz r11,_CCR(r1) /* Load CR */
> - neg r3,r3
> - oris r11,r11,0x1000 /* Set SO bit in CR */
> - stw r11,_CCR(r1)
> -
> -1: stw r6,RESULT(r1) /* Save result */
> - stw r3,GPR3(r1) /* Update return value */
> -2: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
> - beq 4f
> -
> - /* Clear per-syscall TIF flags if any are set. */
> -
> - li r11,_TIF_PERSYSCALL_MASK
> - addi r12,r2,TI_FLAGS
> -3: lwarx r8,0,r12
> - andc r8,r8,r11
> -#ifdef CONFIG_IBM405_ERR77
> - dcbt 0,r12
> -#endif
> - stwcx. r8,0,r12
> - bne- 3b
> -
> -4: /* Anything which requires enabling interrupts? */
> - andi. r0,r9,(_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP)
> - beq ret_from_except
> -
> - /* Re-enable interrupts. There is no need to trace that with
> - * lockdep as we are supposed to have IRQs on at this point
> - */
> - ori r10,r10,MSR_EE
> - SYNC
> - mtmsr r10
> -
> - /* Save NVGPRS if they're not saved already */
> - lwz r4,_TRAP(r1)
> - andi. r4,r4,1
> - beq 5f
> - SAVE_NVGPRS(r1)
> - li r4,0xc00
> - stw r4,_TRAP(r1)
> -5:
> - addi r3,r1,STACK_FRAME_OVERHEAD
> - bl do_syscall_trace_leave
> - b ret_from_except_full
> -
> /*
> * System call was called from kernel. We get here with SRR1 in r9.
> * Mark the exception as recoverable once we have retrieved SRR0,
> diff --git a/arch/powerpc/kernel/head_32.h b/arch/powerpc/kernel/head_32.h
> index 9abec6cd099c..97691931a306 100644
> --- a/arch/powerpc/kernel/head_32.h
> +++ b/arch/powerpc/kernel/head_32.h
> @@ -174,12 +174,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_HPTE_TABLE)
> stw r2,GPR2(r11)
> addi r10,r10,STACK_FRAME_REGS_MARKER@l
> stw r9,_MSR(r11)
> - li r2, \trapno + 1
> + li r2, \trapno
> stw r10,8(r11)
> stw r2,_TRAP(r11)
> SAVE_GPR(0, r11)
> SAVE_4GPRS(3, r11)
> SAVE_2GPRS(7, r11)
> + SAVE_NVGPRS(r11)
> addi r11,r1,STACK_FRAME_OVERHEAD
> addi r2,r12,-THREAD
> stw r11,PT_REGS(r12)
> diff --git a/arch/powerpc/kernel/syscall_64.c b/arch/powerpc/kernel/syscall.c
> similarity index 97%
> rename from arch/powerpc/kernel/syscall_64.c
> rename to arch/powerpc/kernel/syscall.c
> index cf06eb443a80..a07702a85f81 100644
> --- a/arch/powerpc/kernel/syscall_64.c
> +++ b/arch/powerpc/kernel/syscall.c
> @@ -1,8 +1,9 @@
> // SPDX-License-Identifier: GPL-2.0-or-later
>
> #include <linux/err.h>
> +#include <linux/compat.h>
> +
> #include <asm/asm-prototypes.h>
> -#include <asm/book3s/64/kup-radix.h>
> #include <asm/cputime.h>
> #include <asm/hw_irq.h>
> #include <asm/kprobes.h>
> @@ -25,16 +26,20 @@ notrace long system_call_exception(long r3, long r4, long r5,
> unsigned long ti_flags;
> syscall_fn f;
>
> +#ifdef CONFIG_PPC64
> if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG))
> BUG_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED);
> +#endif
>
> trace_hardirqs_off(); /* finish reconciling */
>
> - if (IS_ENABLED(CONFIG_PPC_BOOK3S))
> + if (!IS_ENABLED(CONFIG_PPC_BOOK3E))
> BUG_ON(!(regs->msr & MSR_RI));
> BUG_ON(!(regs->msr & MSR_PR));
> BUG_ON(!FULL_REGS(regs));
> +#ifdef CONFIG_PPC64
> BUG_ON(regs->softe != IRQS_ENABLED);
> +#endif
>
> account_cpu_user_entry();
>
> @@ -48,7 +53,7 @@ notrace long system_call_exception(long r3, long r4, long r5,
> }
> #endif
>
> - kuap_check_amr();
> + kuap_check();
>
> /*
> * This is not required for the syscall exit path, but makes the
> @@ -56,7 +61,9 @@ notrace long system_call_exception(long r3, long r4, long r5,
> * frame, or if the unwinder was taught the first stack frame always
> * returns to user with IRQS_ENABLED, this store could be avoided!
> */
> +#ifdef CONFIG_PPC64
> regs->softe = IRQS_ENABLED;
> +#endif
>
> local_irq_enable();
>
> @@ -86,7 +93,7 @@ notrace long system_call_exception(long r3, long r4, long r5,
> /* May be faster to do array_index_nospec? */
> barrier_nospec();
>
> - if (unlikely(ti_flags & _TIF_32BIT)) {
> + if (is_compat_task()) {
> f = (void *)compat_sys_call_table[r0];
>
> r3 &= 0x00000000ffffffffULL;
> @@ -148,7 +155,9 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
> ret |= _TIF_RESTOREALL;
> }
>
> +#ifdef CONFIG_PPC64
> again:
> +#endif
> local_irq_disable();
> ti_flags = READ_ONCE(*ti_flagsp);
> while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) {
> @@ -191,6 +200,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
>
> /* This pattern matches prep_irq_for_idle */
> __hard_EE_RI_disable();
> +#ifdef CONFIG_PPC64
> if (unlikely(lazy_irq_pending())) {
> __hard_RI_enable();
> trace_hardirqs_off();
> @@ -201,12 +211,13 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
> }
> local_paca->irq_happened = 0;
> irq_soft_mask_set(IRQS_ENABLED);
> +#endif
>
> #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> local_paca->tm_scratch = regs->msr;
> #endif
>
> - kuap_check_amr();
> + kuap_check();
>
> account_cpu_user_exit();
>
> @@ -294,7 +305,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned
> local_paca->tm_scratch = regs->msr;
> #endif
>
> - kuap_check_amr();
> + kuap_check();
>
> account_cpu_user_exit();
>
> @@ -372,7 +383,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsign
> * We don't need to restore AMR on the way back to userspace for KUAP.
> * The value of AMR only matters while we're in the kernel.
> */
> - kuap_restore_amr(regs);
> + kuap_restore(regs);
>
> return ret;
> }
>
^ permalink raw reply
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