* [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 5/7] ASoC: fsl_asrc: Move common definition to fsl_asrc_common
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>
There is a new ASRC included in i.MX serial platform, there
are some common definition can be shared with each other.
So move the common definition to a separate header file.
And add fsl_asrc_pair_priv and fsl_asrc_priv for
the variable specific for the module, which can be used
internally.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_asrc.c | 81 +++++++++++++++---------
sound/soc/fsl/fsl_asrc.h | 74 ++--------------------
sound/soc/fsl/fsl_asrc_common.h | 105 ++++++++++++++++++++++++++++++++
sound/soc/fsl/fsl_asrc_dma.c | 25 ++++----
4 files changed, 176 insertions(+), 109 deletions(-)
create mode 100644 sound/soc/fsl/fsl_asrc_common.h
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index eea19e2b723b..d0b554e818fd 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -308,8 +308,10 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
*/
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{
- struct asrc_config *config = pair->config;
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
+ struct asrc_config *config = pair_priv->config;
struct fsl_asrc *asrc = pair->asrc;
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
enum asrc_pair_index index = pair->index;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
@@ -392,11 +394,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
}
/* Validate input and output clock sources */
- clk_index[IN] = asrc->clk_map[IN][config->inclk];
- clk_index[OUT] = asrc->clk_map[OUT][config->outclk];
+ clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
+ clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
/* We only have output clock for ideal ratio mode */
- clk = asrc->asrck_clk[clk_index[ideal ? OUT : IN]];
+ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
clk_rate = clk_get_rate(clk);
rem[IN] = do_div(clk_rate, inrate);
@@ -417,7 +419,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
div[IN] = min_t(u32, 1024, div[IN]);
- clk = asrc->asrck_clk[clk_index[OUT]];
+ clk = asrc_priv->asrck_clk[clk_index[OUT]];
clk_rate = clk_get_rate(clk);
if (ideal && use_ideal_rate)
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
@@ -437,13 +439,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
/* Set the channel number */
channels = config->channel_num;
- if (asrc->soc->channel_bits < 4)
+ if (asrc_priv->soc->channel_bits < 4)
channels /= 2;
/* Update channels for current pair */
regmap_update_bits(asrc->regmap, REG_ASRCNCR,
- ASRCNCR_ANCi_MASK(index, asrc->soc->channel_bits),
- ASRCNCR_ANCi(index, channels, asrc->soc->channel_bits));
+ ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
+ ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
/* Default setting: Automatic selection for processing mode */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
@@ -568,9 +570,10 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
/* Odd channel number is not valid for older ASRC (channel_bits==3) */
- if (asrc->soc->channel_bits == 3)
+ if (asrc_priv->soc->channel_bits == 3)
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
@@ -586,6 +589,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
@@ -597,7 +601,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- pair->config = &config;
+ pair_priv->config = &config;
config.pair = pair->index;
config.channel_num = channels;
@@ -931,10 +935,16 @@ static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
+{
+ return REG_ASRDx(dir, index);
+}
+
static int fsl_asrc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct fsl_asrc *asrc;
+ struct fsl_asrc_priv *asrc_priv;
struct resource *res;
void __iomem *regs;
int irq, ret, i;
@@ -946,7 +956,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
if (!asrc)
return -ENOMEM;
+ asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
+ if (!asrc_priv)
+ return -ENOMEM;
+
asrc->pdev = pdev;
+ asrc->private = asrc_priv;
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -992,25 +1007,31 @@ static int fsl_asrc_probe(struct platform_device *pdev)
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
- asrc->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
- if (IS_ERR(asrc->asrck_clk[i])) {
+ asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(asrc_priv->asrck_clk[i])) {
dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
- return PTR_ERR(asrc->asrck_clk[i]);
+ return PTR_ERR(asrc_priv->asrck_clk[i]);
}
}
- asrc->soc = of_device_get_match_data(&pdev->dev);
- if (!asrc->soc) {
+ asrc_priv->soc = of_device_get_match_data(&pdev->dev);
+ if (!asrc_priv->soc) {
dev_err(&pdev->dev, "failed to get soc data\n");
return -ENODEV;
}
+ asrc->use_edma = asrc_priv->soc->use_edma;
+ asrc->get_dma_channel = fsl_asrc_get_dma_channel;
+ asrc->request_pair = fsl_asrc_request_pair;
+ asrc->release_pair = fsl_asrc_release_pair;
+ asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
+
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
- asrc->clk_map[IN] = input_clk_map_imx35;
- asrc->clk_map[OUT] = output_clk_map_imx35;
+ asrc_priv->clk_map[IN] = input_clk_map_imx35;
+ asrc_priv->clk_map[OUT] = output_clk_map_imx35;
} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
- asrc->clk_map[IN] = input_clk_map_imx53;
- asrc->clk_map[OUT] = output_clk_map_imx53;
+ asrc_priv->clk_map[IN] = input_clk_map_imx53;
+ asrc_priv->clk_map[OUT] = output_clk_map_imx53;
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
@@ -1024,11 +1045,11 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return -EINVAL;
}
if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
- asrc->clk_map[IN] = clk_map_imx8qm[map_idx];
- asrc->clk_map[OUT] = clk_map_imx8qm[map_idx];
+ asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
+ asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
} else {
- asrc->clk_map[IN] = clk_map_imx8qxp[map_idx];
- asrc->clk_map[OUT] = clk_map_imx8qxp[map_idx];
+ asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
+ asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
}
}
@@ -1092,6 +1113,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
static int fsl_asrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
int i, ret;
ret = clk_prepare_enable(asrc->mem_clk);
@@ -1106,7 +1128,7 @@ static int fsl_asrc_runtime_resume(struct device *dev)
goto disable_ipg_clk;
}
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
- ret = clk_prepare_enable(asrc->asrck_clk[i]);
+ ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
if (ret)
goto disable_asrck_clk;
}
@@ -1115,7 +1137,7 @@ static int fsl_asrc_runtime_resume(struct device *dev)
disable_asrck_clk:
for (i--; i >= 0; i--)
- clk_disable_unprepare(asrc->asrck_clk[i]);
+ clk_disable_unprepare(asrc_priv->asrck_clk[i]);
if (!IS_ERR(asrc->spba_clk))
clk_disable_unprepare(asrc->spba_clk);
disable_ipg_clk:
@@ -1128,10 +1150,11 @@ static int fsl_asrc_runtime_resume(struct device *dev)
static int fsl_asrc_runtime_suspend(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
int i;
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
- clk_disable_unprepare(asrc->asrck_clk[i]);
+ clk_disable_unprepare(asrc_priv->asrck_clk[i]);
if (!IS_ERR(asrc->spba_clk))
clk_disable_unprepare(asrc->spba_clk);
clk_disable_unprepare(asrc->ipg_clk);
@@ -1145,9 +1168,10 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
static int fsl_asrc_suspend(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
regmap_read(asrc->regmap, REG_ASRCFG,
- &asrc->regcache_cfg);
+ &asrc_priv->regcache_cfg);
regcache_cache_only(asrc->regmap, true);
regcache_mark_dirty(asrc->regmap);
@@ -1158,6 +1182,7 @@ static int fsl_asrc_suspend(struct device *dev)
static int fsl_asrc_resume(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
u32 asrctr;
/* Stop all pairs provisionally */
@@ -1171,7 +1196,7 @@ static int fsl_asrc_resume(struct device *dev)
regmap_update_bits(asrc->regmap, REG_ASRCFG,
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
- ASRCFG_PREMODi_ALL_MASK, asrc->regcache_cfg);
+ ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
/* Restart enabled pairs */
regmap_update_bits(asrc->regmap, REG_ASRCTR,
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 3b0f156af2c3..86d2422ad606 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -10,8 +10,7 @@
#ifndef _FSL_ASRC_H
#define _FSL_ASRC_H
-#define IN 0
-#define OUT 1
+#include "fsl_asrc_common.h"
#define ASRC_DMA_BUFFER_NUM 2
#define ASRC_INPUTFIFO_THRESHOLD 32
@@ -283,14 +282,6 @@
#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
-
-enum asrc_pair_index {
- ASRC_INVALID_PAIR = -1,
- ASRC_PAIR_A = 0,
- ASRC_PAIR_B = 1,
- ASRC_PAIR_C = 2,
-};
-
#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
enum asrc_inclk {
@@ -446,83 +437,28 @@ struct fsl_asrc_soc_data {
};
/**
- * fsl_asrc_pair: ASRC Pair private data
+ * fsl_asrc_pair_priv: ASRC Pair private data
*
- * @asrc: pointer to its parent module
* @config: configuration profile
- * @error: error record
- * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
- * @channels: occupied channel number
- * @desc: input and output dma descriptors
- * @dma_chan: inputer and output DMA channels
- * @dma_data: private dma data
- * @pos: hardware pointer position
- * @private: pair private area
*/
-struct fsl_asrc_pair {
- struct fsl_asrc *asrc;
+struct fsl_asrc_pair_priv {
struct asrc_config *config;
- unsigned int error;
-
- enum asrc_pair_index index;
- unsigned int channels;
-
- struct dma_async_tx_descriptor *desc[2];
- struct dma_chan *dma_chan[2];
- struct imx_dma_data dma_data;
- unsigned int pos;
-
- void *private;
};
/**
- * fsl_asrc_pair: ASRC private data
+ * fsl_asrc_priv: ASRC private data
*
- * @dma_params_rx: DMA parameters for receive channel
- * @dma_params_tx: DMA parameters for transmit channel
- * @pdev: platform device pointer
- * @regmap: regmap handler
- * @paddr: physical address to the base address of registers
- * @mem_clk: clock source to access register
- * @ipg_clk: clock source to drive peripheral
- * @spba_clk: SPBA clock (optional, depending on SoC design)
* @asrck_clk: clock sources to driver ASRC internal logic
- * @lock: spin lock for resource protection
- * @pair: pair pointers
* @soc: soc specific data
- * @channel_avail: non-occupied channel numbers
* @clk_map: clock map for input/output clock
- * @asrc_rate: default sample rate for ASoC Back-Ends
- * @asrc_format: default sample format for ASoC Back-Ends
* @regcache_cfg: store register value of REG_ASRCFG
*/
-struct fsl_asrc {
- struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct snd_dmaengine_dai_dma_data dma_params_tx;
- struct platform_device *pdev;
- struct regmap *regmap;
- unsigned long paddr;
- struct clk *mem_clk;
- struct clk *ipg_clk;
- struct clk *spba_clk;
+struct fsl_asrc_priv {
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
- spinlock_t lock;
-
- struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
const struct fsl_asrc_soc_data *soc;
- unsigned int channel_avail;
unsigned char *clk_map[2];
- int asrc_rate;
- snd_pcm_format_t asrc_format;
-
u32 regcache_cfg;
};
-#define DRV_NAME "fsl-asrc-dai"
-extern struct snd_soc_component_driver fsl_asrc_component;
-struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
-int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
-void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
-
#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
new file mode 100644
index 000000000000..5c93ccdfc30a
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 NXP
+ *
+ */
+
+#ifndef _FSL_ASRC_COMMON_H
+#define _FSL_ASRC_COMMON_H
+
+/* directions */
+#define IN 0
+#define OUT 1
+
+enum asrc_pair_index {
+ ASRC_INVALID_PAIR = -1,
+ ASRC_PAIR_A = 0,
+ ASRC_PAIR_B = 1,
+ ASRC_PAIR_C = 2,
+ ASRC_PAIR_D = 3,
+};
+
+#define PAIR_CTX_NUM 0x4
+#define PAIR_PRIVAT_SIZE 0x400
+
+/**
+ * fsl_asrc_pair: ASRC Pair common data
+ *
+ * @asrc: pointer to its parent module
+ * @error: error record
+ * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
+ * @channels: occupied channel number
+ * @desc: input and output dma descriptors
+ * @dma_chan: inputer and output DMA channels
+ * @dma_data: private dma data
+ * @pos: hardware pointer position
+ * @private: pair private area
+ */
+struct fsl_asrc_pair {
+ struct fsl_asrc *asrc;
+ unsigned int error;
+
+ enum asrc_pair_index index;
+ unsigned int channels;
+
+ struct dma_async_tx_descriptor *desc[2];
+ struct dma_chan *dma_chan[2];
+ struct imx_dma_data dma_data;
+ unsigned int pos;
+
+ void *private;
+};
+
+/**
+ * fsl_asrc_pair: ASRC common data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @paddr: physical address to the base address of registers
+ * @mem_clk: clock source to access register
+ * @ipg_clk: clock source to drive peripheral
+ * @spba_clk: SPBA clock (optional, depending on SoC design)
+ * @lock: spin lock for resource protection
+ * @pair: pair pointers
+ * @channel_avail: non-occupied channel numbers
+ * @asrc_rate: default sample rate for ASoC Back-Ends
+ * @asrc_format: default sample format for ASoC Back-Ends
+ * @use_edma: edma is used
+ * @get_dma_channel: function pointer
+ * @request_pair: function pointer
+ * @release_pair: function pointer
+ * @get_fifo_addr: function pointer
+ * @private: private data structure
+ */
+struct fsl_asrc {
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ unsigned long paddr;
+ struct clk *mem_clk;
+ struct clk *ipg_clk;
+ struct clk *spba_clk;
+ spinlock_t lock; /* spin lock for resource protection */
+
+ struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
+ unsigned int channel_avail;
+
+ int asrc_rate;
+ snd_pcm_format_t asrc_format;
+ bool use_edma;
+
+ struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir);
+ int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
+ void (*release_pair)(struct fsl_asrc_pair *pair);
+ int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
+
+ void *private;
+};
+
+#define DRV_NAME "fsl-asrc-dai"
+extern struct snd_soc_component_driver fsl_asrc_component;
+
+#endif /* _FSL_ASRC_COMMON_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index b15946e03380..5cf0468ce6e3 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -12,7 +12,7 @@
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
-#include "fsl_asrc.h"
+#include "fsl_asrc_common.h"
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
@@ -170,10 +170,10 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
/* Override dma_data of the Front-End and config its dmaengine */
dma_params_fe = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
- dma_params_fe->addr = asrc->paddr + REG_ASRDx(!dir, index);
+ dma_params_fe->addr = asrc->paddr + asrc->get_fifo_addr(!dir, index);
dma_params_fe->maxburst = dma_params_be->maxburst;
- pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
+ pair->dma_chan[!dir] = asrc->get_dma_channel(pair, !dir);
if (!pair->dma_chan[!dir]) {
dev_err(dev, "failed to request DMA channel\n");
return -EINVAL;
@@ -203,7 +203,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
* need to configure dma_request and dma_request2, but get dma_chan via
* dma_request_slave_channel directly with dma name of Front-End device
*/
- if (!asrc->soc->use_edma) {
+ if (!asrc->use_edma) {
/* Get DMA request of Back-End */
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
tmp_data = tmp_chan->private;
@@ -211,7 +211,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
dma_release_channel(tmp_chan);
/* Get DMA request of Front-End */
- tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+ tmp_chan = asrc->get_dma_channel(pair, dir);
tmp_data = tmp_chan->private;
pair->dma_data.dma_request2 = tmp_data->dma_request;
pair->dma_data.peripheral_type = tmp_data->peripheral_type;
@@ -222,7 +222,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
dma_request_channel(mask, filter, &pair->dma_data);
} else {
pair->dma_chan[dir] =
- fsl_asrc_get_dma_channel(pair, dir);
+ asrc->get_dma_channel(pair, dir);
}
if (!pair->dma_chan[dir]) {
@@ -251,10 +251,10 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
config_be.dst_maxburst = dma_params_be->maxburst;
if (tx) {
- config_be.src_addr = asrc->paddr + REG_ASRDO(index);
+ config_be.src_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
config_be.dst_addr = dma_params_be->addr;
} else {
- config_be.dst_addr = asrc->paddr + REG_ASRDI(index);
+ config_be.dst_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
config_be.src_addr = dma_params_be->addr;
}
@@ -311,11 +311,12 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
return ret;
}
- pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
+ pair = kzalloc(sizeof(struct fsl_asrc_pair) + PAIR_PRIVAT_SIZE, GFP_KERNEL);
if (!pair)
return -ENOMEM;
pair->asrc = asrc;
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
runtime->private_data = pair;
@@ -323,14 +324,14 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
* Request pair function needs channel num as input, for this
* dummy pair, we just request "1" channel temporarily.
*/
- ret = fsl_asrc_request_pair(1, pair);
+ ret = asrc->request_pair(1, pair);
if (ret < 0) {
dev_err(dev, "failed to request asrc pair\n");
goto req_pair_err;
}
/* Request a dummy dma channel, which will be released later. */
- tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+ tmp_chan = asrc->get_dma_channel(pair, dir);
if (!tmp_chan) {
dev_err(dev, "failed to get dma channel\n");
ret = -EINVAL;
@@ -356,7 +357,7 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
dma_release_channel(tmp_chan);
dma_chan_err:
- fsl_asrc_release_pair(pair);
+ asrc->release_pair(pair);
req_pair_err:
if (release_pair)
--
2.21.0
^ permalink raw reply related
* [PATCH v6 4/7] ASoC: fsl_asrc: Support new property fsl,asrc-format
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>
In order to align with new ESARC, we add new property fsl,asrc-format.
The fsl,asrc-format can replace the fsl,asrc-width, driver
can accept format from devicetree, don't need to convert it to
format through width.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_asrc.c | 40 ++++++++++++++++++++++--------------
sound/soc/fsl/fsl_asrc.h | 4 ++--
sound/soc/fsl/fsl_asrc_dma.c | 15 +++++++++++---
3 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 4d3e51bfa949..eea19e2b723b 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -589,7 +589,6 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
- snd_pcm_format_t format;
int ret;
ret = fsl_asrc_request_pair(channels, pair);
@@ -600,11 +599,6 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
pair->config = &config;
- if (asrc->asrc_width == 16)
- format = SNDRV_PCM_FORMAT_S16_LE;
- else
- format = SNDRV_PCM_FORMAT_S24_LE;
-
config.pair = pair->index;
config.channel_num = channels;
config.inclk = INCLK_NONE;
@@ -612,11 +606,11 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
config.input_format = params_format(params);
- config.output_format = format;
+ config.output_format = asrc->asrc_format;
config.input_sample_rate = rate;
config.output_sample_rate = asrc->asrc_rate;
} else {
- config.input_format = format;
+ config.input_format = asrc->asrc_format;
config.output_format = params_format(params);
config.input_sample_rate = asrc->asrc_rate;
config.output_sample_rate = rate;
@@ -946,6 +940,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
int irq, ret, i;
u32 map_idx;
char tmp[16];
+ u32 width;
asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
if (!asrc)
@@ -1052,16 +1047,31 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return ret;
}
- ret = of_property_read_u32(np, "fsl,asrc-width",
- &asrc->asrc_width);
+ ret = of_property_read_u32(np, "fsl,asrc-format", &asrc->asrc_format);
if (ret) {
- dev_err(&pdev->dev, "failed to get output width\n");
- return ret;
+ ret = of_property_read_u32(np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output width\n");
+ return ret;
+ }
+
+ switch (width) {
+ case 16:
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 24:
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ default:
+ dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ }
}
- if (asrc->asrc_width != 16 && asrc->asrc_width != 24) {
- dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
- asrc->asrc_width = 24;
+ if (!(FSL_ASRC_FORMATS & (1ULL << asrc->asrc_format))) {
+ dev_warn(&pdev->dev, "unsupported format, switching to S24_LE\n");
+ asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
}
platform_set_drvdata(pdev, asrc);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 95d62c45afde..3b0f156af2c3 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -493,7 +493,7 @@ struct fsl_asrc_pair {
* @channel_avail: non-occupied channel numbers
* @clk_map: clock map for input/output clock
* @asrc_rate: default sample rate for ASoC Back-Ends
- * @asrc_width: default sample width for ASoC Back-Ends
+ * @asrc_format: default sample format for ASoC Back-Ends
* @regcache_cfg: store register value of REG_ASRCFG
*/
struct fsl_asrc {
@@ -514,7 +514,7 @@ struct fsl_asrc {
unsigned char *clk_map[2];
int asrc_rate;
- int asrc_width;
+ snd_pcm_format_t asrc_format;
u32 regcache_cfg;
};
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 5fe83aece25b..b15946e03380 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -146,7 +146,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
struct device *dev_be;
u8 dir = tx ? OUT : IN;
dma_cap_mask_t mask;
- int ret;
+ int ret, bits;
/* Fetch the Back-End dma_data from DPCM */
for_each_dpcm_be(rtd, stream, dpcm) {
@@ -230,10 +230,19 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
return -EINVAL;
}
- if (asrc->asrc_width == 16)
+ bits = snd_pcm_format_physical_width(asrc->asrc_format);
+ if (bits < 8 || bits > 64)
+ return -EINVAL;
+ else if (bits == 8)
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ else if (bits == 16)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
- else
+ else if (bits == 24)
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ else if (bits <= 32)
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
config_be.direction = DMA_DEV_TO_DEV;
config_be.src_addr_width = buswidth;
--
2.21.0
^ permalink raw reply related
* [PATCH v6 3/7] ASoC: fsl-asoc-card: Support new property fsl, asrc-format
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>
In order to align with new ESARC, we add new property fsl,asrc-format.
The fsl,asrc-format can replace the fsl,asrc-width, driver
can accept format from devicetree, don't need to convert it to
format through width.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl-asoc-card.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index bb33601fab84..a0f5eb27d61a 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -680,17 +680,20 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto asrc_fail;
}
- ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &priv->asrc_format);
if (ret) {
- dev_err(&pdev->dev, "failed to get output rate\n");
- ret = -EINVAL;
- goto asrc_fail;
- }
+ /* Fallback to old binding; translate to asrc_format */
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output width\n");
+ return ret;
+ }
- if (width == 24)
- priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
- else
- priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ if (width == 24)
+ priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
}
/* Finish card registering */
--
2.21.0
^ permalink raw reply related
* [PATCH v6 2/7] ASoC: dt-bindings: fsl_asrc: Add new property fsl, asrc-format
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>
In order to support new EASRC and simplify the code structure,
We decide to share the common structure between them. This bring
a problem that EASRC accept format directly from devicetree, but
ASRC accept width from devicetree.
In order to align with new ESARC, we add new property fsl,asrc-format.
The fsl,asrc-format can replace the fsl,asrc-width, then driver
can accept format from devicetree, don't need to convert it to
format through width.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
Documentation/devicetree/bindings/sound/fsl,asrc.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
index cb9a25165503..998b4c8a7f78 100644
--- a/Documentation/devicetree/bindings/sound/fsl,asrc.txt
+++ b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
@@ -51,6 +51,10 @@ Optional properties:
will be in use as default. Otherwise, the big endian
mode will be in use for all the device registers.
+ - fsl,asrc-format : Defines a mutual sample format used by DPCM Back
+ Ends, which can replace the fsl,asrc-width.
+ The value is 2 (S16_LE), or 6 (S24_LE).
+
Example:
asrc: asrc@2034000 {
--
2.21.0
^ permalink raw reply related
* [PATCH v6 1/7] ASoC: fsl_asrc: rename asrc_priv to asrc
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>
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>
---
sound/soc/fsl/fsl_asrc.c | 298 +++++++++++++++++------------------
sound/soc/fsl/fsl_asrc.h | 4 +-
sound/soc/fsl/fsl_asrc_dma.c | 24 +--
3 files changed, 163 insertions(+), 163 deletions(-)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 0dcebc24c312..4d3e51bfa949 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -21,10 +21,10 @@
#define IDEAL_RATIO_DECIMAL_DEPTH 26
#define pair_err(fmt, ...) \
- dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+ dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
#define pair_dbg(fmt, ...) \
- dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+ dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
/* Corresponding to process_option */
static unsigned int supported_asrc_rate[] = {
@@ -157,15 +157,15 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{
enum asrc_pair_index index = ASRC_INVALID_PAIR;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
- struct device *dev = &asrc_priv->pdev->dev;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
unsigned long lock_flags;
int i, ret = 0;
- spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+ spin_lock_irqsave(&asrc->lock, lock_flags);
for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
- if (asrc_priv->pair[i] != NULL)
+ if (asrc->pair[i] != NULL)
continue;
index = i;
@@ -177,17 +177,17 @@ int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
if (index == ASRC_INVALID_PAIR) {
dev_err(dev, "all pairs are busy now\n");
ret = -EBUSY;
- } else if (asrc_priv->channel_avail < channels) {
+ } else if (asrc->channel_avail < channels) {
dev_err(dev, "can't afford required channels: %d\n", channels);
ret = -EINVAL;
} else {
- asrc_priv->channel_avail -= channels;
- asrc_priv->pair[index] = pair;
+ asrc->channel_avail -= channels;
+ asrc->pair[index] = pair;
pair->channels = channels;
pair->index = index;
}
- spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+ spin_unlock_irqrestore(&asrc->lock, lock_flags);
return ret;
}
@@ -195,25 +195,25 @@ int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
/**
* Release ASRC pair
*
- * It clears the resource from asrc_priv and releases the occupied channels.
+ * It clears the resource from asrc and releases the occupied channels.
*/
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
unsigned long lock_flags;
/* Make sure the pair is disabled */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), 0);
- spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+ spin_lock_irqsave(&asrc->lock, lock_flags);
- asrc_priv->channel_avail += pair->channels;
- asrc_priv->pair[index] = NULL;
+ asrc->channel_avail += pair->channels;
+ asrc->pair[index] = NULL;
pair->error = 0;
- spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+ spin_unlock_irqrestore(&asrc->lock, lock_flags);
}
/**
@@ -221,10 +221,10 @@ void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
*/
static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
ASRMCRi_EXTTHRSHi_MASK |
ASRMCRi_INFIFO_THRESHOLD_MASK |
ASRMCRi_OUTFIFO_THRESHOLD_MASK,
@@ -257,7 +257,7 @@ static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
int inrate, int outrate)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
unsigned long ratio;
int i;
@@ -286,8 +286,8 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
break;
}
- regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
- regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);
+ regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
+ regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
return 0;
}
@@ -309,7 +309,7 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{
struct asrc_config *config = pair->config;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
@@ -392,11 +392,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
}
/* Validate input and output clock sources */
- clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
- clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
+ clk_index[IN] = asrc->clk_map[IN][config->inclk];
+ clk_index[OUT] = asrc->clk_map[OUT][config->outclk];
/* We only have output clock for ideal ratio mode */
- clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
+ clk = asrc->asrck_clk[clk_index[ideal ? OUT : IN]];
clk_rate = clk_get_rate(clk);
rem[IN] = do_div(clk_rate, inrate);
@@ -417,7 +417,7 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
div[IN] = min_t(u32, 1024, div[IN]);
- clk = asrc_priv->asrck_clk[clk_index[OUT]];
+ clk = asrc->asrck_clk[clk_index[OUT]];
clk_rate = clk_get_rate(clk);
if (ideal && use_ideal_rate)
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
@@ -437,22 +437,22 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
/* Set the channel number */
channels = config->channel_num;
- if (asrc_priv->soc->channel_bits < 4)
+ if (asrc->soc->channel_bits < 4)
channels /= 2;
/* Update channels for current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
- ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
- ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
+ regmap_update_bits(asrc->regmap, REG_ASRCNCR,
+ ASRCNCR_ANCi_MASK(index, asrc->soc->channel_bits),
+ ASRCNCR_ANCi(index, channels, asrc->soc->channel_bits));
/* Default setting: Automatic selection for processing mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_USRi_MASK(index), 0);
/* Set the input and output clock sources */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
+ regmap_update_bits(asrc->regmap, REG_ASRCSR,
ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
ASRCSR_AICS(index, clk_index[IN]) |
ASRCSR_AOCS(index, clk_index[OUT]));
@@ -462,19 +462,19 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
/* Implement word_width configurations */
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
ASRMCR1i_OW16(output_word_width) |
ASRMCR1i_IWD(input_word_width));
/* Enable BUFFER STALL */
- regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
/* Set default thresholds for input and output FIFO */
@@ -486,18 +486,18 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
return 0;
/* Clear ASTSx bit to use Ideal Ratio mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), 0);
/* Enable Ideal Ratio mode */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
ASRCTR_IDR(index) | ASRCTR_USR(index));
fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
/* Apply configurations for pre- and post-processing */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+ regmap_update_bits(asrc->regmap, REG_ASRCFG,
ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index),
ASRCFG_PREMOD(index, pre_proc) |
ASRCFG_POSTMOD(index, post_proc));
@@ -512,28 +512,28 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
*/
static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
int reg, retry = 10, i;
/* Enable the current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
/* Wait for status of initialization */
do {
udelay(5);
- regmap_read(asrc_priv->regmap, REG_ASRCFG, ®);
+ regmap_read(asrc->regmap, REG_ASRCFG, ®);
reg &= ASRCFG_INIRQi_MASK(index);
} while (!reg && --retry);
/* Make the input fifo to ASRC STALL level */
- regmap_read(asrc_priv->regmap, REG_ASRCNCR, ®);
+ regmap_read(asrc->regmap, REG_ASRCNCR, ®);
for (i = 0; i < pair->channels * 4; i++)
- regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0);
+ regmap_write(asrc->regmap, REG_ASRDI(index), 0);
/* Enable overload interrupt */
- regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE);
+ regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
}
/**
@@ -541,11 +541,11 @@ static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
*/
static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
/* Stop the current pair */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_MASK(index), 0);
}
@@ -554,23 +554,23 @@ static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
*/
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
{
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
char name[4];
sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
- return dma_request_slave_channel(&asrc_priv->pdev->dev, name);
+ return dma_request_slave_channel(&asrc->pdev->dev, name);
}
EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
/* Odd channel number is not valid for older ASRC (channel_bits==3) */
- if (asrc_priv->soc->channel_bits == 3)
+ if (asrc->soc->channel_bits == 3)
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
@@ -583,7 +583,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
@@ -600,7 +600,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
pair->config = &config;
- if (asrc_priv->asrc_width == 16)
+ if (asrc->asrc_width == 16)
format = SNDRV_PCM_FORMAT_S16_LE;
else
format = SNDRV_PCM_FORMAT_S24_LE;
@@ -614,11 +614,11 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
config.input_format = params_format(params);
config.output_format = format;
config.input_sample_rate = rate;
- config.output_sample_rate = asrc_priv->asrc_rate;
+ config.output_sample_rate = asrc->asrc_rate;
} else {
config.input_format = format;
config.output_format = params_format(params);
- config.input_sample_rate = asrc_priv->asrc_rate;
+ config.input_sample_rate = asrc->asrc_rate;
config.output_sample_rate = rate;
}
@@ -676,10 +676,10 @@ static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
{
- struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx,
- &asrc_priv->dma_params_rx);
+ snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
+ &asrc->dma_params_rx);
return 0;
}
@@ -858,30 +858,30 @@ static const struct regmap_config fsl_asrc_regmap_config = {
/**
* Initialize ASRC registers with a default configurations
*/
-static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
+static int fsl_asrc_init(struct fsl_asrc *asrc)
{
/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
- regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
+ regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
/* Disable interrupt by default */
- regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0);
+ regmap_write(asrc->regmap, REG_ASRIER, 0x0);
/* Apply recommended settings for parameters from Reference Manual */
- regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff);
- regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555);
- regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280);
- regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280);
- regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
+ regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
+ regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
+ regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
/* Base address for task queue FIFO. Set to 0x7C */
- regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
+ regmap_update_bits(asrc->regmap, REG_ASRTFR1,
ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
/* Set the processing clock for 76KHz to 133M */
- regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
+ regmap_write(asrc->regmap, REG_ASR76K, 0x06D6);
/* Set the processing clock for 56KHz to 133M */
- return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
+ return regmap_write(asrc->regmap, REG_ASR56K, 0x0947);
}
/**
@@ -889,15 +889,15 @@ static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
*/
static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
{
- struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id;
- struct device *dev = &asrc_priv->pdev->dev;
+ struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
+ struct device *dev = &asrc->pdev->dev;
enum asrc_pair_index index;
u32 status;
- regmap_read(asrc_priv->regmap, REG_ASRSTR, &status);
+ regmap_read(asrc->regmap, REG_ASRSTR, &status);
/* Clean overload error */
- regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE);
+ regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
/*
* We here use dev_dbg() for all exceptions because ASRC itself does
@@ -905,31 +905,31 @@ static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
* interrupt would result a ridged conversion.
*/
for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
- if (!asrc_priv->pair[index])
+ if (!asrc->pair[index])
continue;
if (status & ASRSTR_ATQOL) {
- asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
}
if (status & ASRSTR_AOOL(index)) {
- asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
pair_dbg("Output Task Overload\n");
}
if (status & ASRSTR_AIOL(index)) {
- asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
+ asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
pair_dbg("Input Task Overload\n");
}
if (status & ASRSTR_AODO(index)) {
- asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
+ asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
pair_dbg("Output Data Buffer has overflowed\n");
}
if (status & ASRSTR_AIDU(index)) {
- asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
+ asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
pair_dbg("Input Data Buffer has underflowed\n");
}
}
@@ -940,18 +940,18 @@ static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
static int fsl_asrc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct fsl_asrc *asrc_priv;
+ struct fsl_asrc *asrc;
struct resource *res;
void __iomem *regs;
int irq, ret, i;
u32 map_idx;
char tmp[16];
- asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
- if (!asrc_priv)
+ asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
+ if (!asrc)
return -ENOMEM;
- asrc_priv->pdev = pdev;
+ asrc->pdev = pdev;
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -959,13 +959,13 @@ static int fsl_asrc_probe(struct platform_device *pdev)
if (IS_ERR(regs))
return PTR_ERR(regs);
- asrc_priv->paddr = res->start;
+ asrc->paddr = res->start;
- asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
- &fsl_asrc_regmap_config);
- if (IS_ERR(asrc_priv->regmap)) {
+ asrc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
+ &fsl_asrc_regmap_config);
+ if (IS_ERR(asrc->regmap)) {
dev_err(&pdev->dev, "failed to init regmap\n");
- return PTR_ERR(asrc_priv->regmap);
+ return PTR_ERR(asrc->regmap);
}
irq = platform_get_irq(pdev, 0);
@@ -973,49 +973,49 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return irq;
ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
- dev_name(&pdev->dev), asrc_priv);
+ dev_name(&pdev->dev), asrc);
if (ret) {
dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
return ret;
}
- asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem");
- if (IS_ERR(asrc_priv->mem_clk)) {
+ asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
+ if (IS_ERR(asrc->mem_clk)) {
dev_err(&pdev->dev, "failed to get mem clock\n");
- return PTR_ERR(asrc_priv->mem_clk);
+ return PTR_ERR(asrc->mem_clk);
}
- asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(asrc_priv->ipg_clk)) {
+ asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(asrc->ipg_clk)) {
dev_err(&pdev->dev, "failed to get ipg clock\n");
- return PTR_ERR(asrc_priv->ipg_clk);
+ return PTR_ERR(asrc->ipg_clk);
}
- asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
- if (IS_ERR(asrc_priv->spba_clk))
+ asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
+ if (IS_ERR(asrc->spba_clk))
dev_warn(&pdev->dev, "failed to get spba clock\n");
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
- asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
- if (IS_ERR(asrc_priv->asrck_clk[i])) {
+ asrc->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(asrc->asrck_clk[i])) {
dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
- return PTR_ERR(asrc_priv->asrck_clk[i]);
+ return PTR_ERR(asrc->asrck_clk[i]);
}
}
- asrc_priv->soc = of_device_get_match_data(&pdev->dev);
- if (!asrc_priv->soc) {
+ asrc->soc = of_device_get_match_data(&pdev->dev);
+ if (!asrc->soc) {
dev_err(&pdev->dev, "failed to get soc data\n");
return -ENODEV;
}
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
- asrc_priv->clk_map[IN] = input_clk_map_imx35;
- asrc_priv->clk_map[OUT] = output_clk_map_imx35;
+ asrc->clk_map[IN] = input_clk_map_imx35;
+ asrc->clk_map[OUT] = output_clk_map_imx35;
} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
- asrc_priv->clk_map[IN] = input_clk_map_imx53;
- asrc_priv->clk_map[OUT] = output_clk_map_imx53;
+ asrc->clk_map[IN] = input_clk_map_imx53;
+ asrc->clk_map[OUT] = output_clk_map_imx53;
} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
@@ -1029,44 +1029,44 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return -EINVAL;
}
if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
- asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
- asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
+ asrc->clk_map[IN] = clk_map_imx8qm[map_idx];
+ asrc->clk_map[OUT] = clk_map_imx8qm[map_idx];
} else {
- asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
- asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
+ asrc->clk_map[IN] = clk_map_imx8qxp[map_idx];
+ asrc->clk_map[OUT] = clk_map_imx8qxp[map_idx];
}
}
- ret = fsl_asrc_init(asrc_priv);
+ ret = fsl_asrc_init(asrc);
if (ret) {
dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
return ret;
}
- asrc_priv->channel_avail = 10;
+ asrc->channel_avail = 10;
ret = of_property_read_u32(np, "fsl,asrc-rate",
- &asrc_priv->asrc_rate);
+ &asrc->asrc_rate);
if (ret) {
dev_err(&pdev->dev, "failed to get output rate\n");
return ret;
}
ret = of_property_read_u32(np, "fsl,asrc-width",
- &asrc_priv->asrc_width);
+ &asrc->asrc_width);
if (ret) {
dev_err(&pdev->dev, "failed to get output width\n");
return ret;
}
- if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
+ if (asrc->asrc_width != 16 && asrc->asrc_width != 24) {
dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
- asrc_priv->asrc_width = 24;
+ asrc->asrc_width = 24;
}
- platform_set_drvdata(pdev, asrc_priv);
+ platform_set_drvdata(pdev, asrc);
pm_runtime_enable(&pdev->dev);
- spin_lock_init(&asrc_priv->lock);
+ spin_lock_init(&asrc->lock);
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
&fsl_asrc_dai, 1);
@@ -1081,22 +1081,22 @@ static int fsl_asrc_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
static int fsl_asrc_runtime_resume(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
int i, ret;
- ret = clk_prepare_enable(asrc_priv->mem_clk);
+ ret = clk_prepare_enable(asrc->mem_clk);
if (ret)
return ret;
- ret = clk_prepare_enable(asrc_priv->ipg_clk);
+ ret = clk_prepare_enable(asrc->ipg_clk);
if (ret)
goto disable_mem_clk;
- if (!IS_ERR(asrc_priv->spba_clk)) {
- ret = clk_prepare_enable(asrc_priv->spba_clk);
+ if (!IS_ERR(asrc->spba_clk)) {
+ ret = clk_prepare_enable(asrc->spba_clk);
if (ret)
goto disable_ipg_clk;
}
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
- ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
+ ret = clk_prepare_enable(asrc->asrck_clk[i]);
if (ret)
goto disable_asrck_clk;
}
@@ -1105,27 +1105,27 @@ static int fsl_asrc_runtime_resume(struct device *dev)
disable_asrck_clk:
for (i--; i >= 0; i--)
- clk_disable_unprepare(asrc_priv->asrck_clk[i]);
- if (!IS_ERR(asrc_priv->spba_clk))
- clk_disable_unprepare(asrc_priv->spba_clk);
+ clk_disable_unprepare(asrc->asrck_clk[i]);
+ if (!IS_ERR(asrc->spba_clk))
+ clk_disable_unprepare(asrc->spba_clk);
disable_ipg_clk:
- clk_disable_unprepare(asrc_priv->ipg_clk);
+ clk_disable_unprepare(asrc->ipg_clk);
disable_mem_clk:
- clk_disable_unprepare(asrc_priv->mem_clk);
+ clk_disable_unprepare(asrc->mem_clk);
return ret;
}
static int fsl_asrc_runtime_suspend(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
int i;
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
- clk_disable_unprepare(asrc_priv->asrck_clk[i]);
- if (!IS_ERR(asrc_priv->spba_clk))
- clk_disable_unprepare(asrc_priv->spba_clk);
- clk_disable_unprepare(asrc_priv->ipg_clk);
- clk_disable_unprepare(asrc_priv->mem_clk);
+ clk_disable_unprepare(asrc->asrck_clk[i]);
+ if (!IS_ERR(asrc->spba_clk))
+ clk_disable_unprepare(asrc->spba_clk);
+ clk_disable_unprepare(asrc->ipg_clk);
+ clk_disable_unprepare(asrc->mem_clk);
return 0;
}
@@ -1134,37 +1134,37 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
#ifdef CONFIG_PM_SLEEP
static int fsl_asrc_suspend(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
- regmap_read(asrc_priv->regmap, REG_ASRCFG,
- &asrc_priv->regcache_cfg);
+ regmap_read(asrc->regmap, REG_ASRCFG,
+ &asrc->regcache_cfg);
- regcache_cache_only(asrc_priv->regmap, true);
- regcache_mark_dirty(asrc_priv->regmap);
+ regcache_cache_only(asrc->regmap, true);
+ regcache_mark_dirty(asrc->regmap);
return 0;
}
static int fsl_asrc_resume(struct device *dev)
{
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
u32 asrctr;
/* Stop all pairs provisionally */
- regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, 0);
/* Restore all registers */
- regcache_cache_only(asrc_priv->regmap, false);
- regcache_sync(asrc_priv->regmap);
+ regcache_cache_only(asrc->regmap, false);
+ regcache_sync(asrc->regmap);
- regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+ regmap_update_bits(asrc->regmap, REG_ASRCFG,
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
- ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
+ ASRCFG_PREMODi_ALL_MASK, asrc->regcache_cfg);
/* Restart enabled pairs */
- regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ASRCEi_ALL_MASK, asrctr);
return 0;
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 8a821132d9d0..95d62c45afde 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -448,7 +448,7 @@ struct fsl_asrc_soc_data {
/**
* fsl_asrc_pair: ASRC Pair private data
*
- * @asrc_priv: pointer to its parent module
+ * @asrc: pointer to its parent module
* @config: configuration profile
* @error: error record
* @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
@@ -460,7 +460,7 @@ struct fsl_asrc_soc_data {
* @private: pair private area
*/
struct fsl_asrc_pair {
- struct fsl_asrc *asrc_priv;
+ struct fsl_asrc *asrc;
struct asrc_config *config;
unsigned int error;
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index e7178817d7a7..5fe83aece25b 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -135,7 +135,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct fsl_asrc *asrc = pair->asrc;
struct dma_slave_config config_fe, config_be;
enum asrc_pair_index index = pair->index;
struct device *dev = component->dev;
@@ -170,7 +170,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
/* Override dma_data of the Front-End and config its dmaengine */
dma_params_fe = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
- dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index);
+ dma_params_fe->addr = asrc->paddr + REG_ASRDx(!dir, index);
dma_params_fe->maxburst = dma_params_be->maxburst;
pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
@@ -203,7 +203,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
* need to configure dma_request and dma_request2, but get dma_chan via
* dma_request_slave_channel directly with dma name of Front-End device
*/
- if (!asrc_priv->soc->use_edma) {
+ if (!asrc->soc->use_edma) {
/* Get DMA request of Back-End */
tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
tmp_data = tmp_chan->private;
@@ -230,7 +230,7 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
return -EINVAL;
}
- if (asrc_priv->asrc_width == 16)
+ if (asrc->asrc_width == 16)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
else
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -242,10 +242,10 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
config_be.dst_maxburst = dma_params_be->maxburst;
if (tx) {
- config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index);
+ config_be.src_addr = asrc->paddr + REG_ASRDO(index);
config_be.dst_addr = dma_params_be->addr;
} else {
- config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index);
+ config_be.dst_addr = asrc->paddr + REG_ASRDI(index);
config_be.src_addr = dma_params_be->addr;
}
@@ -288,7 +288,7 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dmaengine_dai_dma_data *dma_data;
struct device *dev = component->dev;
- struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
struct fsl_asrc_pair *pair;
struct dma_chan *tmp_chan = NULL;
u8 dir = tx ? OUT : IN;
@@ -306,7 +306,7 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
if (!pair)
return -ENOMEM;
- pair->asrc_priv = asrc_priv;
+ pair->asrc = asrc;
runtime->private_data = pair;
@@ -361,15 +361,15 @@ static int fsl_asrc_dma_shutdown(struct snd_soc_component *component,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct fsl_asrc *asrc_priv;
+ struct fsl_asrc *asrc;
if (!pair)
return 0;
- asrc_priv = pair->asrc_priv;
+ asrc = pair->asrc;
- if (asrc_priv->pair[pair->index] == pair)
- asrc_priv->pair[pair->index] = NULL;
+ if (asrc->pair[pair->index] == pair)
+ asrc->pair[pair->index] = NULL;
kfree(pair);
--
2.21.0
^ permalink raw reply related
* [PATCH v6 0/7] ASoC: Add new module driver for new ASRC
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
Add new module driver for new ASRC in i.MX8MN, several commits
are added for new property fsl,asrc-format
Shengjiu Wang (7):
ASoC: fsl_asrc: rename asrc_priv to asrc
ASoC: dt-bindings: fsl_asrc: Add new property fsl,asrc-format
ASoC: fsl-asoc-card: Support new property fsl,asrc-format
ASoC: fsl_asrc: Support new property fsl,asrc-format
ASoC: fsl_asrc: Move common definition to fsl_asrc_common
ASoC: dt-bindings: fsl_easrc: Add document for EASRC
ASoC: fsl_easrc: Add EASRC ASoC CPU DAI drivers
changes in v6
- updated according to Nicoin's and Rob's comments.
changes in v5
- Add new property fsl,asrc-format, rather than change fsl,asrc-width
to fsl,asrc-formt.
- code change for above change.
changes in v4
- Add several commit for changing DT binding asrc-width to asrc-format
- rename asrc_priv to asrc
changes in v3
- add new commit "ASoC: fsl_asrc: Change asrc_width to asrc_format"
- modify binding doc to yaml format
- remove fsl_easrc_dma.c, make fsl_asrc_dma.c useable for easrc.
changes in v2
- change i.MX815 to i.MX8MN
- Add changes in Kconfig and Makefile
.../devicetree/bindings/sound/fsl,asrc.txt | 4 +
.../devicetree/bindings/sound/fsl,easrc.yaml | 101 +
sound/soc/fsl/Kconfig | 11 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl-asoc-card.c | 21 +-
sound/soc/fsl/fsl_asrc.c | 303 +--
sound/soc/fsl/fsl_asrc.h | 74 +-
sound/soc/fsl/fsl_asrc_common.h | 105 +
sound/soc/fsl/fsl_asrc_dma.c | 54 +-
sound/soc/fsl/fsl_easrc.c | 2114 +++++++++++++++++
sound/soc/fsl/fsl_easrc.h | 651 +++++
11 files changed, 3206 insertions(+), 234 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,easrc.yaml
create mode 100644 sound/soc/fsl/fsl_asrc_common.h
create mode 100644 sound/soc/fsl/fsl_easrc.c
create mode 100644 sound/soc/fsl/fsl_easrc.h
--
2.21.0
^ permalink raw reply
* Re: [PATCH v4 10/25] nvdimm: Add driver for OpenCAPI Persistent Memory
From: Dan Williams @ 2020-04-01 8:49 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-11-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> This driver exposes LPC memory on OpenCAPI pmem cards
> as an NVDIMM, allowing the existing nvram infrastructure
> to be used.
>
> Namespace metadata is stored on the media itself, so
> scm_reserve_metadata() maps 1 section's worth of PMEM storage
> at the start to hold this. The rest of the PMEM range is registered
> with libnvdimm as an nvdimm. ndctl_config_read/write/size() provide
> callbacks to libnvdimm to access the metadata.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
> drivers/nvdimm/Kconfig | 2 +
> drivers/nvdimm/Makefile | 1 +
> drivers/nvdimm/ocxl/Kconfig | 15 ++
> drivers/nvdimm/ocxl/Makefile | 7 +
> drivers/nvdimm/ocxl/main.c | 476 +++++++++++++++++++++++++++++++++
> drivers/nvdimm/ocxl/ocxlpmem.h | 23 ++
> 6 files changed, 524 insertions(+)
> create mode 100644 drivers/nvdimm/ocxl/Kconfig
> create mode 100644 drivers/nvdimm/ocxl/Makefile
> create mode 100644 drivers/nvdimm/ocxl/main.c
> create mode 100644 drivers/nvdimm/ocxl/ocxlpmem.h
>
> diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
> index b7d1eb38b27d..368328637182 100644
> --- a/drivers/nvdimm/Kconfig
> +++ b/drivers/nvdimm/Kconfig
> @@ -131,4 +131,6 @@ config NVDIMM_TEST_BUILD
> core devm_memremap_pages() implementation and other
> infrastructure.
>
> +source "drivers/nvdimm/ocxl/Kconfig"
> +
> endif
> diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
> index 29203f3d3069..bc02be11c794 100644
> --- a/drivers/nvdimm/Makefile
> +++ b/drivers/nvdimm/Makefile
> @@ -33,3 +33,4 @@ libnvdimm-$(CONFIG_NVDIMM_KEYS) += security.o
> TOOLS := ../../tools
> TEST_SRC := $(TOOLS)/testing/nvdimm/test
> obj-$(CONFIG_NVDIMM_TEST_BUILD) += $(TEST_SRC)/iomap.o
> +obj-$(CONFIG_LIBNVDIMM) += ocxl/
> diff --git a/drivers/nvdimm/ocxl/Kconfig b/drivers/nvdimm/ocxl/Kconfig
> new file mode 100644
> index 000000000000..c5d927520920
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +if LIBNVDIMM
> +
> +config OCXL_PMEM
> + tristate "OpenCAPI Persistent Memory"
> + depends on LIBNVDIMM && PPC_POWERNV && PCI && EEH && ZONE_DEVICE && OCXL
Does OXCL_PMEM itself have any CONFIG_ZONE_DEVICE dependencies? That's
more a function of CONFIG_DEV_DAX and CONFIG_FS_DAX. Doesn't OCXL
already depend on CONFIG_PCI?
> + help
> + Exposes devices that implement the OpenCAPI Storage Class Memory
> + specification as persistent memory regions. You may also want
> + DEV_DAX, DEV_DAX_PMEM & FS_DAX if you plan on using DAX devices
> + stacked on top of this driver.
> +
> + Select N if unsure.
> +
> +endif
> diff --git a/drivers/nvdimm/ocxl/Makefile b/drivers/nvdimm/ocxl/Makefile
> new file mode 100644
> index 000000000000..e0e8ade1987a
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +ccflags-$(CONFIG_PPC_WERROR) += -Werror
> +
> +obj-$(CONFIG_OCXL_PMEM) += ocxlpmem.o
> +
> +ocxlpmem-y := main.o
> \ No newline at end of file
> diff --git a/drivers/nvdimm/ocxl/main.c b/drivers/nvdimm/ocxl/main.c
> new file mode 100644
> index 000000000000..c0066fedf9cc
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl/main.c
> @@ -0,0 +1,476 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2020 IBM Corp.
> +
> +/*
> + * A driver for OpenCAPI devices that implement the Storage Class
> + * Memory specification.
> + */
> +
> +#include <linux/module.h>
> +#include <misc/ocxl.h>
> +#include <linux/ndctl.h>
> +#include <linux/mm_types.h>
> +#include <linux/memory_hotplug.h>
> +#include "ocxlpmem.h"
> +
> +static const struct pci_device_id pci_tbl[] = {
> + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(pci, pci_tbl);
> +
> +#define NUM_MINORS 256 // Total to reserve
> +
> +static dev_t ocxlpmem_dev;
> +static struct class *ocxlpmem_class;
> +static struct mutex minors_idr_lock;
> +static struct idr minors_idr;
> +
> +/**
> + * ndctl_config_write() - Handle a ND_CMD_SET_CONFIG_DATA command from ndctl
> + * @ocxlpmem: the device metadata
> + * @command: the incoming data to write
> + * Return: 0 on success, negative on failure
> + */
> +static int ndctl_config_write(struct ocxlpmem *ocxlpmem,
> + struct nd_cmd_set_config_hdr *command)
> +{
> + if (command->in_offset + command->in_length > LABEL_AREA_SIZE)
> + return -EINVAL;
> +
> + memcpy_flushcache(ocxlpmem->metadata_addr + command->in_offset,
> + command->in_buf, command->in_length);
> +
> + return 0;
> +}
> +
> +/**
> + * ndctl_config_read() - Handle a ND_CMD_GET_CONFIG_DATA command from ndctl
> + * @ocxlpmem: the device metadata
> + * @command: the read request
> + * Return: 0 on success, negative on failure
> + */
> +static int ndctl_config_read(struct ocxlpmem *ocxlpmem,
> + struct nd_cmd_get_config_data_hdr *command)
> +{
> + if (command->in_offset + command->in_length > LABEL_AREA_SIZE)
> + return -EINVAL;
> +
> + memcpy_mcsafe(command->out_buf,
> + ocxlpmem->metadata_addr + command->in_offset,
> + command->in_length);
> +
> + return 0;
> +}
> +
> +/**
> + * ndctl_config_size() - Handle a ND_CMD_GET_CONFIG_SIZE command from ndctl
> + * @command: the read request
> + * Return: 0 on success, negative on failure
> + */
> +static int ndctl_config_size(struct nd_cmd_get_config_size *command)
> +{
> + command->status = 0;
> + command->config_size = LABEL_AREA_SIZE;
> + command->max_xfer = PAGE_SIZE;
> +
> + return 0;
> +}
> +
> +static int ndctl(struct nvdimm_bus_descriptor *nd_desc,
> + struct nvdimm *nvdimm,
> + unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
> +{
> + struct ocxlpmem *ocxlpmem = container_of(nd_desc,
> + struct ocxlpmem, bus_desc);
> +
> + switch (cmd) {
> + case ND_CMD_GET_CONFIG_SIZE:
> + *cmd_rc = ndctl_config_size(buf);
> + return 0;
> +
> + case ND_CMD_GET_CONFIG_DATA:
> + *cmd_rc = ndctl_config_read(ocxlpmem, buf);
> + return 0;
> +
> + case ND_CMD_SET_CONFIG_DATA:
> + *cmd_rc = ndctl_config_write(ocxlpmem, buf);
> + return 0;
> +
> + default:
> + return -ENOTTY;
> + }
> +}
> +
> +/**
> + * reserve_metadata() - Reserve space for nvdimm metadata
> + * @ocxlpmem: the device metadata
> + * @lpc_mem: The resource representing the LPC memory of the OpenCAPI device
> + */
> +static int reserve_metadata(struct ocxlpmem *ocxlpmem,
> + struct resource *lpc_mem)
> +{
> + ocxlpmem->metadata_addr = devm_memremap(&ocxlpmem->dev, lpc_mem->start,
> + LABEL_AREA_SIZE, MEMREMAP_WB);
> + if (IS_ERR(ocxlpmem->metadata_addr))
> + return PTR_ERR(ocxlpmem->metadata_addr);
> +
> + return 0;
> +}
> +
> +/**
> + * register_lpc_mem() - Discover persistent memory on a device and register it with the NVDIMM subsystem
> + * @ocxlpmem: the device metadata
> + * Return: 0 on success
> + */
> +static int register_lpc_mem(struct ocxlpmem *ocxlpmem)
> +{
> + struct nd_region_desc region_desc;
> + struct nd_mapping_desc nd_mapping_desc;
> + struct resource *lpc_mem;
> + const struct ocxl_afu_config *config;
> + const struct ocxl_fn_config *fn_config;
> + int rc;
> + unsigned long nvdimm_cmd_mask = 0;
> + unsigned long nvdimm_flags = 0;
> + int target_node;
> + char serial[16 + 1];
> +
> + // Set up the reserved metadata area
> + rc = ocxl_afu_map_lpc_mem(ocxlpmem->ocxl_afu);
> + if (rc < 0)
> + return rc;
> +
> + lpc_mem = ocxl_afu_lpc_mem(ocxlpmem->ocxl_afu);
> + if (!lpc_mem || !lpc_mem->start)
> + return -EINVAL;
> +
> + config = ocxl_afu_config(ocxlpmem->ocxl_afu);
> + fn_config = ocxl_function_config(ocxlpmem->ocxl_fn);
> +
> + rc = reserve_metadata(ocxlpmem, lpc_mem);
> + if (rc)
> + return rc;
> +
> + ocxlpmem->bus_desc.provider_name = "ocxlpmem";
> + ocxlpmem->bus_desc.ndctl = ndctl;
> + ocxlpmem->bus_desc.module = THIS_MODULE;
> +
> + ocxlpmem->nvdimm_bus = nvdimm_bus_register(&ocxlpmem->dev,
> + &ocxlpmem->bus_desc);
> + if (!ocxlpmem->nvdimm_bus)
> + return -EINVAL;
> +
> + ocxlpmem->pmem_res.start = (u64)lpc_mem->start + LABEL_AREA_SIZE;
> + ocxlpmem->pmem_res.end = (u64)lpc_mem->start + config->lpc_mem_size - 1;
> + ocxlpmem->pmem_res.name = "OpenCAPI persistent memory";
> +
> + set_bit(ND_CMD_GET_CONFIG_SIZE, &nvdimm_cmd_mask);
> + set_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm_cmd_mask);
> + set_bit(ND_CMD_SET_CONFIG_DATA, &nvdimm_cmd_mask);
> +
> + set_bit(NDD_ALIASING, &nvdimm_flags);
> +
> + snprintf(serial, sizeof(serial), "%llx", fn_config->serial);
> + nd_mapping_desc.nvdimm = nvdimm_create(ocxlpmem->nvdimm_bus, ocxlpmem,
> + NULL, nvdimm_flags,
> + nvdimm_cmd_mask, 0, NULL);
> + if (!nd_mapping_desc.nvdimm)
> + return -ENOMEM;
> +
> + if (nvdimm_bus_check_dimm_count(ocxlpmem->nvdimm_bus, 1))
> + return -EINVAL;
> +
> + nd_mapping_desc.start = ocxlpmem->pmem_res.start;
> + nd_mapping_desc.size = resource_size(&ocxlpmem->pmem_res);
> + nd_mapping_desc.position = 0;
> +
> + ocxlpmem->nd_set.cookie1 = fn_config->serial;
> + ocxlpmem->nd_set.cookie2 = fn_config->serial;
> +
> + target_node = of_node_to_nid(ocxlpmem->pdev->dev.of_node);
> +
> + memset(®ion_desc, 0, sizeof(region_desc));
> + region_desc.res = &ocxlpmem->pmem_res;
> + region_desc.numa_node = NUMA_NO_NODE;
> + region_desc.target_node = target_node;
> + region_desc.num_mappings = 1;
> + region_desc.mapping = &nd_mapping_desc;
> + region_desc.nd_set = &ocxlpmem->nd_set;
> +
> + set_bit(ND_REGION_PAGEMAP, ®ion_desc.flags);
> + /*
> + * NB: libnvdimm copies the data from ndr_desc into it's own
> + * structures so passing a stack pointer is fine.
> + */
> + ocxlpmem->nd_region = nvdimm_pmem_region_create(ocxlpmem->nvdimm_bus,
> + ®ion_desc);
> + if (!ocxlpmem->nd_region)
> + return -EINVAL;
> +
> + dev_info(&ocxlpmem->dev,
> + "Onlining %lluMB of persistent memory\n",
> + nd_mapping_desc.size / SZ_1M);
> +
> + return 0;
> +}
> +
> +/**
> + * allocate_minor() - Allocate a minor number to use for an OpenCAPI pmem device
> + * @ocxlpmem: the device metadata
> + * Return: the allocated minor number
> + */
> +static int allocate_minor(struct ocxlpmem *ocxlpmem)
> +{
> + int minor;
> +
> + mutex_lock(&minors_idr_lock);
> + minor = idr_alloc(&minors_idr, ocxlpmem, 0, NUM_MINORS, GFP_KERNEL);
> + mutex_unlock(&minors_idr_lock);
> + return minor;
> +}
> +
> +static void free_minor(struct ocxlpmem *ocxlpmem)
> +{
> + mutex_lock(&minors_idr_lock);
> + idr_remove(&minors_idr, MINOR(ocxlpmem->dev.devt));
> + mutex_unlock(&minors_idr_lock);
> +}
> +
> +/**
> + * free_ocxlpmem() - Free all members of an ocxlpmem struct
> + * @ocxlpmem: the device struct to clear
> + */
> +static void free_ocxlpmem(struct ocxlpmem *ocxlpmem)
> +{
> + int rc;
> +
> + free_minor(ocxlpmem);
> +
> + if (ocxlpmem->ocxl_context) {
> + rc = ocxl_context_detach(ocxlpmem->ocxl_context);
> + if (rc == -EBUSY)
> + dev_warn(&ocxlpmem->dev, "Timeout detaching ocxl context\n");
> + else
> + ocxl_context_free(ocxlpmem->ocxl_context);
> + }
> +
> + if (ocxlpmem->ocxl_afu)
> + ocxl_afu_put(ocxlpmem->ocxl_afu);
> +
> + if (ocxlpmem->ocxl_fn)
> + ocxl_function_close(ocxlpmem->ocxl_fn);
> +
> + pci_dev_put(ocxlpmem->pdev);
> +
> + kfree(ocxlpmem);
> +}
> +
> +/**
> + * free_ocxlpmem_dev() - Free an OpenCAPI persistent memory device
> + * @dev: The device struct
> + */
> +static void free_ocxlpmem_dev(struct device *dev)
> +{
> + struct ocxlpmem *ocxlpmem = container_of(dev, struct ocxlpmem, dev);
> +
> + free_ocxlpmem(ocxlpmem);
> +}
> +
> +/**
> + * ocxlpmem_register() - Register an OpenCAPI pmem device with the kernel
> + * @ocxlpmem: the device metadata
> + * Return: 0 on success, negative on failure
> + */
> +static int ocxlpmem_register(struct ocxlpmem *ocxlpmem)
> +{
> + int rc;
> + int minor = allocate_minor(ocxlpmem);
> +
> + if (minor < 0)
> + return minor;
> +
> + ocxlpmem->dev.release = free_ocxlpmem_dev;
> + rc = dev_set_name(&ocxlpmem->dev, "ocxlpmem%d", minor);
> + if (rc < 0)
> + return rc;
> +
> + ocxlpmem->dev.devt = MKDEV(MAJOR(ocxlpmem_dev), minor);
> + ocxlpmem->dev.class = ocxlpmem_class;
> + ocxlpmem->dev.parent = &ocxlpmem->pdev->dev;
> +
> + return device_register(&ocxlpmem->dev);
You lost me, why does this need it's own ioctl path and device node?
I'd expect you you'd tunnel the commands you need through the existing
infrastructure ideally with an eye for cross-arch compatibility. This
is a fundamental concern that probably has significant implications
for what follows.
> +}
> +
> +/**
> + * ocxlpmem_remove() - Free an OpenCAPI persistent memory device
> + * @pdev: the PCI device information struct
> + */
> +static void remove(struct pci_dev *pdev)
> +{
> + if (PCI_FUNC(pdev->devfn) == 0) {
> + struct ocxl_fn *func0 = pci_get_drvdata(pdev);
> +
> + if (func0)
> + ocxl_function_close(func0);
> + } else {
> + struct ocxlpmem *ocxlpmem = pci_get_drvdata(pdev);
> +
> + if (!ocxlpmem)
> + return;
> +
> + if (ocxlpmem->nvdimm_bus)
> + nvdimm_bus_unregister(ocxlpmem->nvdimm_bus);
> +
> + device_unregister(&ocxlpmem->dev);
> + }
> +}
> +
> +/**
> + * probe_function0() - Set up function 0 for an OpenCAPI persistent memory device
> + * This is important as it enables templates higher than 0 across all other
> + * functions, which in turn enables higher bandwidth accesses
> + * @pdev: the PCI device information struct
> + * Return: 0 on success, negative on failure
> + */
> +static int probe_function0(struct pci_dev *pdev)
> +{
> + struct ocxl_fn *fn;
> +
> + fn = ocxl_function_open(pdev);
> + if (IS_ERR(fn)) {
> + dev_err(&pdev->dev, "failed to open OCXL function\n");
> + return PTR_ERR(fn);
> + }
> +
> + pci_set_drvdata(pdev, fn);
> +
> + return 0;
> +}
> +
> +/**
> + * probe() - Init an OpenCAPI persistent memory device
> + * @pdev: the PCI device information struct
> + * @ent: The entry from ocxlpmem_pci_tbl
> + * Return: 0 on success, negative on failure
> + */
> +static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> + struct ocxlpmem *ocxlpmem;
> + int rc;
> +
> + if (PCI_FUNC(pdev->devfn) == 0)
> + return probe_function0(pdev);
> + else if (PCI_FUNC(pdev->devfn) != 1)
> + return 0;
> +
> + ocxlpmem = kzalloc(sizeof(*ocxlpmem), GFP_KERNEL);
> + if (!ocxlpmem) {
> + rc = -ENOMEM;
> + goto err;
> + }
> +
> + ocxlpmem->pdev = pci_dev_get(pdev);
> +
> + pci_set_drvdata(pdev, ocxlpmem);
> +
> + ocxlpmem->ocxl_fn = ocxl_function_open(pdev);
> + if (IS_ERR(ocxlpmem->ocxl_fn)) {
> + rc = PTR_ERR(ocxlpmem->ocxl_fn);
> + dev_err(&pdev->dev, "failed to open OCXL function\n");
> + goto err_unregistered;
> + }
> +
> + ocxlpmem->ocxl_afu = ocxl_function_fetch_afu(ocxlpmem->ocxl_fn, 0);
> + if (!ocxlpmem->ocxl_afu) {
> + rc = -ENXIO;
> + dev_err(&pdev->dev, "Could not get OCXL AFU from function\n");
> + goto err_unregistered;
> + }
> +
> + ocxl_afu_get(ocxlpmem->ocxl_afu);
> +
> + // Resources allocated below here are cleaned up in the release handler
> +
> + rc = ocxlpmem_register(ocxlpmem);
> + if (rc) {
> + dev_err(&pdev->dev,
> + "Could not register OpenCAPI persistent memory device with the kernel\n");
> + goto err;
> + }
> +
> + rc = ocxl_context_alloc(&ocxlpmem->ocxl_context, ocxlpmem->ocxl_afu,
> + NULL);
> + if (rc) {
> + dev_err(&pdev->dev, "Could not allocate OCXL context\n");
> + goto err;
> + }
> +
> + rc = ocxl_context_attach(ocxlpmem->ocxl_context, 0, NULL);
> + if (rc) {
> + dev_err(&pdev->dev, "Could not attach ocxl context\n");
> + goto err;
> + }
> +
> + rc = register_lpc_mem(ocxlpmem);
> + if (rc) {
> + dev_err(&pdev->dev,
> + "Could not register OpenCAPI persistent memory with libnvdimm\n");
> + goto err;
> + }
> +
> + return 0;
> +
> +err_unregistered:
> + if (!IS_ERR(ocxlpmem->ocxl_fn))
> + ocxl_function_close(ocxlpmem->ocxl_fn);
> + pci_dev_put(ocxlpmem->pdev);
> + kfree(ocxlpmem);
> + pci_set_drvdata(pdev, NULL);
> +
> +err:
> + /*
> + * Further cleanup is done in the release handler via free_ocxlpmem()
> + * This allows us to keep the character device live to handle IOCTLs to
> + * investigate issues if the card has an error
> + */
> +
> + dev_err(&pdev->dev,
> + "Error detected, will not register OpenCAPI persistent memory\n");
> + return 0;
> +}
> +
> +static struct pci_driver pci_driver = {
> + .name = "ocxlpmem",
> + .id_table = pci_tbl,
> + .probe = probe,
> + .remove = remove,
> + .shutdown = remove,
> +};
> +
> +static int __init ocxlpmem_init(void)
> +{
> + int rc;
> +
> + mutex_init(&minors_idr_lock);
> + idr_init(&minors_idr);
> +
> + rc = pci_register_driver(&pci_driver);
> + if (rc)
> + return rc;
> +
> + return 0;
> +}
> +
> +static void ocxlpmem_exit(void)
> +{
> + pci_unregister_driver(&pci_driver);
> +}
> +
> +module_init(ocxlpmem_init);
> +module_exit(ocxlpmem_exit);
> +
> +MODULE_DESCRIPTION("OpenCAPI Persistent Memory");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Alastair D'Silva <alastair@d-silva.org>");
> diff --git a/drivers/nvdimm/ocxl/ocxlpmem.h b/drivers/nvdimm/ocxl/ocxlpmem.h
> new file mode 100644
> index 000000000000..03fe7a264281
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl/ocxlpmem.h
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2020 IBM Corp.
> +
> +#include <linux/pci.h>
> +#include <misc/ocxl.h>
> +#include <linux/libnvdimm.h>
> +#include <linux/mm.h>
> +
> +#define LABEL_AREA_SIZE BIT_ULL(PA_SECTION_SHIFT)
> +
> +struct ocxlpmem {
> + struct device dev;
> + struct pci_dev *pdev;
> + struct ocxl_fn *ocxl_fn;
> + struct nd_interleave_set nd_set;
> + struct nvdimm_bus_descriptor bus_desc;
> + struct nvdimm_bus *nvdimm_bus;
> + struct ocxl_afu *ocxl_afu;
> + struct ocxl_context *ocxl_context;
> + void *metadata_addr;
> + struct resource pmem_res;
> + struct nd_region *nd_region;
> +};
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 08/25] ocxl: Emit a log message showing how much LPC memory was detected
From: Dan Williams @ 2020-04-01 8:49 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-9-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> This patch emits a message showing how much LPC memory & special purpose
> memory was detected on an OCXL device.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>
> Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
> ---
> drivers/misc/ocxl/config.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
> index a62e3d7db2bf..69cca341d446 100644
> --- a/drivers/misc/ocxl/config.c
> +++ b/drivers/misc/ocxl/config.c
> @@ -568,6 +568,10 @@ static int read_afu_lpc_memory_info(struct pci_dev *dev,
> afu->special_purpose_mem_size =
> total_mem_size - lpc_mem_size;
> }
> +
> + dev_info(&dev->dev, "Probed LPC memory of %#llx bytes and special purpose memory of %#llx bytes\n",
> + afu->lpc_mem_size, afu->special_purpose_mem_size);
A patch for a single log message is too fine grained for my taste,
let's squash this into another patch in the series.
> +
> return 0;
> }
>
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 07/25] ocxl: Add functions to map/unmap LPC memory
From: Dan Williams @ 2020-04-01 8:49 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-8-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> Add functions to map/unmap LPC memory
>
"map memory" is an overloaded term. I'm guessing this patch has
nothing to do with mapping memory in the MMU. Is it updating hardware
resource decoders to start claiming address space that was allocated
previously?
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>
> ---
> drivers/misc/ocxl/core.c | 51 +++++++++++++++++++++++++++++++
> drivers/misc/ocxl/ocxl_internal.h | 3 ++
> include/misc/ocxl.h | 21 +++++++++++++
> 3 files changed, 75 insertions(+)
>
> diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
> index 2531c6cf19a0..75ff14e3882a 100644
> --- a/drivers/misc/ocxl/core.c
> +++ b/drivers/misc/ocxl/core.c
> @@ -210,6 +210,56 @@ static void unmap_mmio_areas(struct ocxl_afu *afu)
> release_fn_bar(afu->fn, afu->config.global_mmio_bar);
> }
>
> +int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu)
> +{
> + struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> +
> + if ((afu->config.lpc_mem_size + afu->config.special_purpose_mem_size) == 0)
> + return 0;
> +
> + afu->lpc_base_addr = ocxl_link_lpc_map(afu->fn->link, dev);
> + if (afu->lpc_base_addr == 0)
> + return -EINVAL;
> +
> + if (afu->config.lpc_mem_size > 0) {
> + afu->lpc_res.start = afu->lpc_base_addr + afu->config.lpc_mem_offset;
> + afu->lpc_res.end = afu->lpc_res.start + afu->config.lpc_mem_size - 1;
> + }
> +
> + if (afu->config.special_purpose_mem_size > 0) {
> + afu->special_purpose_res.start = afu->lpc_base_addr +
> + afu->config.special_purpose_mem_offset;
> + afu->special_purpose_res.end = afu->special_purpose_res.start +
> + afu->config.special_purpose_mem_size - 1;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(ocxl_afu_map_lpc_mem);
> +
> +struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu)
> +{
> + return &afu->lpc_res;
> +}
> +EXPORT_SYMBOL_GPL(ocxl_afu_lpc_mem);
> +
> +static void unmap_lpc_mem(struct ocxl_afu *afu)
> +{
> + struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> +
> + if (afu->lpc_res.start || afu->special_purpose_res.start) {
> + void *link = afu->fn->link;
> +
> + // only release the link when the the last consumer calls release
> + ocxl_link_lpc_release(link, dev);
> +
> + afu->lpc_res.start = 0;
> + afu->lpc_res.end = 0;
> + afu->special_purpose_res.start = 0;
> + afu->special_purpose_res.end = 0;
> + }
> +}
> +
> static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
> {
> int rc;
> @@ -251,6 +301,7 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
>
> static void deconfigure_afu(struct ocxl_afu *afu)
> {
> + unmap_lpc_mem(afu);
> unmap_mmio_areas(afu);
> reclaim_afu_pasid(afu);
> reclaim_afu_actag(afu);
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 2d7575225bd7..7b975a89db7b 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -52,6 +52,9 @@ struct ocxl_afu {
> void __iomem *global_mmio_ptr;
> u64 pp_mmio_start;
> void *private;
> + u64 lpc_base_addr; /* Covers both LPC & special purpose memory */
> + struct resource lpc_res;
> + struct resource special_purpose_res;
> };
>
> enum ocxl_context_status {
> diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
> index 357ef1aadbc0..d8b0b4d46bfb 100644
> --- a/include/misc/ocxl.h
> +++ b/include/misc/ocxl.h
> @@ -203,6 +203,27 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
>
> // AFU Metadata
>
> +/**
> + * ocxl_afu_map_lpc_mem() - Map the LPC system & special purpose memory for an AFU
> + * Do not call this during device discovery, as there may me multiple
s/me/be/
> + * devices on a link, and the memory is mapped for the whole link, not
> + * just one device. It should only be called after all devices have
> + * registered their memory on the link.
> + *
> + * @afu: The AFU that has the LPC memory to map
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu);
> +
> +/**
> + * ocxl_afu_lpc_mem() - Get the physical address range of LPC memory for an AFU
> + * @afu: The AFU associated with the LPC memory
> + *
> + * Returns a pointer to the resource struct for the physical address range
> + */
> +struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu);
> +
> /**
> * ocxl_afu_config() - Get a pointer to the config for an AFU
> * @afu: a pointer to the AFU to get the config for
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 05/25] ocxl: Address kernel doc errors & warnings
From: Dan Williams @ 2020-04-01 8:49 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-6-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> This patch addresses warnings and errors from the kernel doc scripts for
> the OpenCAPI driver.
>
> It also makes minor tweaks to make the docs more consistent.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
> ---
> drivers/misc/ocxl/config.c | 24 ++++----
> drivers/misc/ocxl/ocxl_internal.h | 9 +--
> include/misc/ocxl.h | 96 ++++++++++++-------------------
> 3 files changed, 55 insertions(+), 74 deletions(-)
Looks good.
>
> diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
> index c8e19bfb5ef9..a62e3d7db2bf 100644
> --- a/drivers/misc/ocxl/config.c
> +++ b/drivers/misc/ocxl/config.c
> @@ -273,16 +273,16 @@ static int read_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn,
> }
>
> /**
> - * Read the template version from the AFU
> - * dev: the device for the AFU
> - * fn: the AFU offsets
> - * len: outputs the template length
> - * version: outputs the major<<8,minor version
> + * read_template_version() - Read the template version from the AFU
> + * @dev: the device for the AFU
> + * @fn: the AFU offsets
> + * @len: outputs the template length
> + * @version: outputs the major<<8,minor version
> *
> * Returns 0 on success, negative on failure
> */
> static int read_template_version(struct pci_dev *dev, struct ocxl_fn_config *fn,
> - u16 *len, u16 *version)
> + u16 *len, u16 *version)
> {
> u32 val32;
> u8 major, minor;
> @@ -476,16 +476,16 @@ static int validate_afu(struct pci_dev *dev, struct ocxl_afu_config *afu)
> }
>
> /**
> - * Populate AFU metadata regarding LPC memory
> - * dev: the device for the AFU
> - * fn: the AFU offsets
> - * afu: the AFU struct to populate the LPC metadata into
> + * read_afu_lpc_memory_info() - Populate AFU metadata regarding LPC memory
> + * @dev: the device for the AFU
> + * @fn: the AFU offsets
> + * @afu: the AFU struct to populate the LPC metadata into
> *
> * Returns 0 on success, negative on failure
> */
> static int read_afu_lpc_memory_info(struct pci_dev *dev,
> - struct ocxl_fn_config *fn,
> - struct ocxl_afu_config *afu)
> + struct ocxl_fn_config *fn,
> + struct ocxl_afu_config *afu)
> {
> int rc;
> u32 val32;
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 345bf843a38e..198e4e4bc51d 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -122,11 +122,12 @@ int ocxl_config_check_afu_index(struct pci_dev *dev,
> struct ocxl_fn_config *fn, int afu_idx);
>
> /**
> - * Update values within a Process Element
> + * ocxl_link_update_pe() - Update values within a Process Element
> + * @link_handle: the link handle associated with the process element
> + * @pasid: the PASID for the AFU context
> + * @tid: the new thread id for the process element
> *
> - * link_handle: the link handle associated with the process element
> - * pasid: the PASID for the AFU context
> - * tid: the new thread id for the process element
> + * Returns 0 on success
> */
> int ocxl_link_update_pe(void *link_handle, int pasid, __u16 tid);
>
> diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
> index 0a762e387418..357ef1aadbc0 100644
> --- a/include/misc/ocxl.h
> +++ b/include/misc/ocxl.h
> @@ -62,8 +62,7 @@ struct ocxl_context;
> // Device detection & initialisation
>
> /**
> - * Open an OpenCAPI function on an OpenCAPI device
> - *
> + * ocxl_function_open() - Open an OpenCAPI function on an OpenCAPI device
> * @dev: The PCI device that contains the function
> *
> * Returns an opaque pointer to the function, or an error pointer (check with IS_ERR)
> @@ -71,8 +70,7 @@ struct ocxl_context;
> struct ocxl_fn *ocxl_function_open(struct pci_dev *dev);
>
> /**
> - * Get the list of AFUs associated with a PCI function device
> - *
> + * ocxl_function_afu_list() - Get the list of AFUs associated with a PCI function device
> * Returns a list of struct ocxl_afu *
> *
> * @fn: The OpenCAPI function containing the AFUs
> @@ -80,8 +78,7 @@ struct ocxl_fn *ocxl_function_open(struct pci_dev *dev);
> struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn);
>
> /**
> - * Fetch an AFU instance from an OpenCAPI function
> - *
> + * ocxl_function_fetch_afu() - Fetch an AFU instance from an OpenCAPI function
> * @fn: The OpenCAPI function to get the AFU from
> * @afu_idx: The index of the AFU to get
> *
> @@ -92,23 +89,20 @@ struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn);
> struct ocxl_afu *ocxl_function_fetch_afu(struct ocxl_fn *fn, u8 afu_idx);
>
> /**
> - * Take a reference to an AFU
> - *
> + * ocxl_afu_get() - Take a reference to an AFU
> * @afu: The AFU to increment the reference count on
> */
> void ocxl_afu_get(struct ocxl_afu *afu);
>
> /**
> - * Release a reference to an AFU
> - *
> + * ocxl_afu_put() - Release a reference to an AFU
> * @afu: The AFU to decrement the reference count on
> */
> void ocxl_afu_put(struct ocxl_afu *afu);
>
>
> /**
> - * Get the configuration information for an OpenCAPI function
> - *
> + * ocxl_function_config() - Get the configuration information for an OpenCAPI function
> * @fn: The OpenCAPI function to get the config for
> *
> * Returns the function config, or NULL on error
> @@ -116,8 +110,7 @@ void ocxl_afu_put(struct ocxl_afu *afu);
> const struct ocxl_fn_config *ocxl_function_config(struct ocxl_fn *fn);
>
> /**
> - * Close an OpenCAPI function
> - *
> + * ocxl_function_close() - Close an OpenCAPI function
> * This will free any AFUs previously retrieved from the function, and
> * detach and associated contexts. The contexts must by freed by the caller.
> *
> @@ -129,8 +122,7 @@ void ocxl_function_close(struct ocxl_fn *fn);
> // Context allocation
>
> /**
> - * Allocate an OpenCAPI context
> - *
> + * ocxl_context_alloc() - Allocate an OpenCAPI context
> * @context: The OpenCAPI context to allocate, must be freed with ocxl_context_free
> * @afu: The AFU the context belongs to
> * @mapping: The mapping to unmap when the context is closed (may be NULL)
> @@ -139,14 +131,13 @@ int ocxl_context_alloc(struct ocxl_context **context, struct ocxl_afu *afu,
> struct address_space *mapping);
>
> /**
> - * Free an OpenCAPI context
> - *
> + * ocxl_context_free() - Free an OpenCAPI context
> * @ctx: The OpenCAPI context to free
> */
> void ocxl_context_free(struct ocxl_context *ctx);
>
> /**
> - * Grant access to an MM to an OpenCAPI context
> + * ocxl_context_attach() - Grant access to an MM to an OpenCAPI context
> * @ctx: The OpenCAPI context to attach
> * @amr: The value of the AMR register to restrict access
> * @mm: The mm to attach to the context
> @@ -157,7 +148,7 @@ int ocxl_context_attach(struct ocxl_context *ctx, u64 amr,
> struct mm_struct *mm);
>
> /**
> - * Detach an MM from an OpenCAPI context
> + * ocxl_context_detach() - Detach an MM from an OpenCAPI context
> * @ctx: The OpenCAPI context to attach
> *
> * Returns 0 on success, negative on failure
> @@ -167,7 +158,7 @@ int ocxl_context_detach(struct ocxl_context *ctx);
> // AFU IRQs
>
> /**
> - * Allocate an IRQ associated with an AFU context
> + * ocxl_afu_irq_alloc() - Allocate an IRQ associated with an AFU context
> * @ctx: the AFU context
> * @irq_id: out, the IRQ ID
> *
> @@ -176,7 +167,7 @@ int ocxl_context_detach(struct ocxl_context *ctx);
> int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id);
>
> /**
> - * Frees an IRQ associated with an AFU context
> + * ocxl_afu_irq_free() - Frees an IRQ associated with an AFU context
> * @ctx: the AFU context
> * @irq_id: the IRQ ID
> *
> @@ -185,7 +176,7 @@ int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id);
> int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id);
>
> /**
> - * Gets the address of the trigger page for an IRQ
> + * ocxl_afu_irq_get_addr() - Gets the address of the trigger page for an IRQ
> * This can then be provided to an AFU which will write to that
> * page to trigger the IRQ.
> * @ctx: The AFU context that the IRQ is associated with
> @@ -196,7 +187,7 @@ int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id);
> u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id);
>
> /**
> - * Provide a callback to be called when an IRQ is triggered
> + * ocxl_irq_set_handler() - Provide a callback to be called when an IRQ is triggered
> * @ctx: The AFU context that the IRQ is associated with
> * @irq_id: The IRQ ID
> * @handler: the callback to be called when the IRQ is triggered
> @@ -213,8 +204,7 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
> // AFU Metadata
>
> /**
> - * Get a pointer to the config for an AFU
> - *
> + * ocxl_afu_config() - Get a pointer to the config for an AFU
> * @afu: a pointer to the AFU to get the config for
> *
> * Returns a pointer to the AFU config
> @@ -222,27 +212,24 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
> struct ocxl_afu_config *ocxl_afu_config(struct ocxl_afu *afu);
>
> /**
> - * Assign opaque hardware specific information to an OpenCAPI AFU.
> - *
> - * @dev: The PCI device associated with the OpenCAPI device
> + * ocxl_afu_set_private() - Assign opaque hardware specific information to an OpenCAPI AFU.
> + * @afu: The OpenCAPI AFU
> * @private: the opaque hardware specific information to assign to the driver
> */
> void ocxl_afu_set_private(struct ocxl_afu *afu, void *private);
>
> /**
> - * Fetch the hardware specific information associated with an external OpenCAPI
> - * AFU. This may be consumed by an external OpenCAPI driver.
> - *
> - * @afu: The AFU
> + * ocxl_afu_get_private() - Fetch the hardware specific information associated with
> + * an external OpenCAPI AFU. This may be consumed by an external OpenCAPI driver.
> + * @afu: The OpenCAPI AFU
> *
> * Returns the opaque pointer associated with the device, or NULL if not set
> */
> -void *ocxl_afu_get_private(struct ocxl_afu *dev);
> +void *ocxl_afu_get_private(struct ocxl_afu *afu);
>
> // Global MMIO
> /**
> - * Read a 32 bit value from global MMIO
> - *
> + * ocxl_global_mmio_read32() - Read a 32 bit value from global MMIO
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -251,11 +238,10 @@ void *ocxl_afu_get_private(struct ocxl_afu *dev);
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_read32(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u32 *val);
> + enum ocxl_endian endian, u32 *val);
>
> /**
> - * Read a 64 bit value from global MMIO
> - *
> + * ocxl_global_mmio_read64() - Read a 64 bit value from global MMIO
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -264,11 +250,10 @@ int ocxl_global_mmio_read32(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_read64(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u64 *val);
> + enum ocxl_endian endian, u64 *val);
>
> /**
> - * Write a 32 bit value to global MMIO
> - *
> + * ocxl_global_mmio_write32() - Write a 32 bit value to global MMIO
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -277,11 +262,10 @@ int ocxl_global_mmio_read64(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_write32(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u32 val);
> + enum ocxl_endian endian, u32 val);
>
> /**
> - * Write a 64 bit value to global MMIO
> - *
> + * ocxl_global_mmio_write64() - Write a 64 bit value to global MMIO
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -290,11 +274,10 @@ int ocxl_global_mmio_write32(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_write64(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u64 val);
> + enum ocxl_endian endian, u64 val);
>
> /**
> - * Set bits in a 32 bit global MMIO register
> - *
> + * ocxl_global_mmio_set32() - Set bits in a 32 bit global MMIO register
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -303,11 +286,10 @@ int ocxl_global_mmio_write64(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_set32(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u32 mask);
> + enum ocxl_endian endian, u32 mask);
>
> /**
> - * Set bits in a 64 bit global MMIO register
> - *
> + * ocxl_global_mmio_set64() - Set bits in a 64 bit global MMIO register
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -316,11 +298,10 @@ int ocxl_global_mmio_set32(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_set64(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u64 mask);
> + enum ocxl_endian endian, u64 mask);
>
> /**
> - * Set bits in a 32 bit global MMIO register
> - *
> + * ocxl_global_mmio_clear32() - Set bits in a 32 bit global MMIO register
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -329,11 +310,10 @@ int ocxl_global_mmio_set64(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_clear32(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u32 mask);
> + enum ocxl_endian endian, u32 mask);
>
> /**
> - * Set bits in a 64 bit global MMIO register
> - *
> + * ocxl_global_mmio_clear64() - Set bits in a 64 bit global MMIO register
> * @afu: The AFU
> * @offset: The Offset from the start of MMIO
> * @endian: the endianness that the MMIO data is in
> @@ -342,7 +322,7 @@ int ocxl_global_mmio_clear32(struct ocxl_afu *afu, size_t offset,
> * Returns 0 for success, negative on error
> */
> int ocxl_global_mmio_clear64(struct ocxl_afu *afu, size_t offset,
> - enum ocxl_endian endian, u64 mask);
> + enum ocxl_endian endian, u64 mask);
>
> // Functions left here are for compatibility with the cxlflash driver
>
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 06/25] ocxl: Tally up the LPC memory on a link & allow it to be mapped
From: Dan Williams @ 2020-04-01 8:48 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-7-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:53 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> OpenCAPI LPC memory is allocated per link, but each link supports
> multiple AFUs, and each AFU can have LPC memory assigned to it.
Is there an OpenCAPI primer to decode these objects and their
associations that I can reference?
>
> This patch tallys the memory for all AFUs on a link, allowing it
> to be mapped in a single operation after the AFUs have been
> enumerated.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
> drivers/misc/ocxl/core.c | 10 ++++++
> drivers/misc/ocxl/link.c | 60 +++++++++++++++++++++++++++++++
> drivers/misc/ocxl/ocxl_internal.h | 33 +++++++++++++++++
> 3 files changed, 103 insertions(+)
>
> diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
> index b7a09b21ab36..2531c6cf19a0 100644
> --- a/drivers/misc/ocxl/core.c
> +++ b/drivers/misc/ocxl/core.c
> @@ -230,8 +230,18 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
> if (rc)
> goto err_free_pasid;
>
> + if (afu->config.lpc_mem_size || afu->config.special_purpose_mem_size) {
> + rc = ocxl_link_add_lpc_mem(afu->fn->link, afu->config.lpc_mem_offset,
> + afu->config.lpc_mem_size +
> + afu->config.special_purpose_mem_size);
> + if (rc)
> + goto err_free_mmio;
> + }
> +
> return 0;
>
> +err_free_mmio:
> + unmap_mmio_areas(afu);
> err_free_pasid:
> reclaim_afu_pasid(afu);
> err_free_actag:
> diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c
> index 58d111afd9f6..af119d3ef79a 100644
> --- a/drivers/misc/ocxl/link.c
> +++ b/drivers/misc/ocxl/link.c
> @@ -84,6 +84,11 @@ struct ocxl_link {
> int dev;
> atomic_t irq_available;
> struct spa *spa;
> + struct mutex lpc_mem_lock; /* protects lpc_mem & lpc_mem_sz */
> + u64 lpc_mem_sz; /* Total amount of LPC memory presented on the link */
> + u64 lpc_mem;
> + int lpc_consumers;
> +
> void *platform_data;
> };
> static struct list_head links_list = LIST_HEAD_INIT(links_list);
> @@ -396,6 +401,8 @@ static int alloc_link(struct pci_dev *dev, int PE_mask, struct ocxl_link **out_l
> if (rc)
> goto err_spa;
>
> + mutex_init(&link->lpc_mem_lock);
> +
> /* platform specific hook */
> rc = pnv_ocxl_spa_setup(dev, link->spa->spa_mem, PE_mask,
> &link->platform_data);
> @@ -711,3 +718,56 @@ void ocxl_link_free_irq(void *link_handle, int hw_irq)
> atomic_inc(&link->irq_available);
> }
> EXPORT_SYMBOL_GPL(ocxl_link_free_irq);
> +
> +int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size)
> +{
> + struct ocxl_link *link = (struct ocxl_link *)link_handle;
> +
> + // Check for overflow
> + if (offset > (offset + size))
> + return -EINVAL;
> +
> + mutex_lock(&link->lpc_mem_lock);
> + link->lpc_mem_sz = max(link->lpc_mem_sz, offset + size);
> +
> + mutex_unlock(&link->lpc_mem_lock);
> +
> + return 0;
> +}
> +
> +u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev)
> +{
> + struct ocxl_link *link = (struct ocxl_link *)link_handle;
> +
> + mutex_lock(&link->lpc_mem_lock);
> +
> + if (!link->lpc_mem)
> + link->lpc_mem = pnv_ocxl_platform_lpc_setup(pdev, link->lpc_mem_sz);
> +
> + if (link->lpc_mem)
> + link->lpc_consumers++;
> + mutex_unlock(&link->lpc_mem_lock);
> +
> + return link->lpc_mem;
> +}
> +
> +void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev)
> +{
> + struct ocxl_link *link = (struct ocxl_link *)link_handle;
> +
> + mutex_lock(&link->lpc_mem_lock);
> +
> + if (!link->lpc_mem) {
> + mutex_unlock(&link->lpc_mem_lock);
> + return;
> + }
> +
> + WARN_ON(--link->lpc_consumers < 0);
> +
> + if (link->lpc_consumers == 0) {
> + pnv_ocxl_platform_lpc_release(pdev);
> + link->lpc_mem = 0;
> + }
> +
> + mutex_unlock(&link->lpc_mem_lock);
> +}
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 198e4e4bc51d..2d7575225bd7 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -142,4 +142,37 @@ int ocxl_irq_offset_to_id(struct ocxl_context *ctx, u64 offset);
> u64 ocxl_irq_id_to_offset(struct ocxl_context *ctx, int irq_id);
> void ocxl_afu_irq_free_all(struct ocxl_context *ctx);
>
> +/**
> + * ocxl_link_add_lpc_mem() - Increment the amount of memory required by an OpenCAPI link
> + *
> + * @link_handle: The OpenCAPI link handle
> + * @offset: The offset of the memory to add
> + * @size: The number of bytes to increment memory on the link by
> + *
> + * Returns 0 on success, -EINVAL on overflow
> + */
> +int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size);
> +
> +/**
> + * ocxl_link_lpc_map() - Map the LPC memory for an OpenCAPI device
> + * Since LPC memory belongs to a link, the whole LPC memory available
> + * on the link must be mapped in order to make it accessible to a device.
> + * @link_handle: The OpenCAPI link handle
> + * @pdev: A device that is on the link
> + *
> + * Returns the address of the mapped LPC memory, or 0 on error
> + */
> +u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev);
> +
> +/**
> + * ocxl_link_lpc_release() - Release the LPC memory device for an OpenCAPI device
> + *
> + * Offlines LPC memory on an OpenCAPI link for a device. If this is the
> + * last device on the link to release the memory, unmap it from the link.
> + *
> + * @link_handle: The OpenCAPI link handle
> + * @pdev: A device that is on the link
> + */
> +void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev);
> +
> #endif /* _OCXL_INTERNAL_H_ */
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 04/25] ocxl: Remove unnecessary externs
From: Dan Williams @ 2020-04-01 8:48 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-5-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> Function declarations don't need externs, remove the existing ones
> so they are consistent with newer code
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
> Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>
Looks good.
> ---
> arch/powerpc/include/asm/pnv-ocxl.h | 40 ++++++++++++++---------------
> include/misc/ocxl.h | 6 ++---
> 2 files changed, 22 insertions(+), 24 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
> index 560a19bb71b7..205efc41a33c 100644
> --- a/arch/powerpc/include/asm/pnv-ocxl.h
> +++ b/arch/powerpc/include/asm/pnv-ocxl.h
> @@ -9,29 +9,27 @@
> #define PNV_OCXL_TL_BITS_PER_RATE 4
> #define PNV_OCXL_TL_RATE_BUF_SIZE ((PNV_OCXL_TL_MAX_TEMPLATE+1) * PNV_OCXL_TL_BITS_PER_RATE / 8)
>
> -extern int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled,
> - u16 *supported);
> -extern int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count);
> +int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, u16 *supported);
> +int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count);
>
> -extern int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap,
> +int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap,
> char *rate_buf, int rate_buf_size);
> -extern int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap,
> - uint64_t rate_buf_phys, int rate_buf_size);
> -
> -extern int pnv_ocxl_get_xsl_irq(struct pci_dev *dev, int *hwirq);
> -extern void pnv_ocxl_unmap_xsl_regs(void __iomem *dsisr, void __iomem *dar,
> - void __iomem *tfc, void __iomem *pe_handle);
> -extern int pnv_ocxl_map_xsl_regs(struct pci_dev *dev, void __iomem **dsisr,
> - void __iomem **dar, void __iomem **tfc,
> - void __iomem **pe_handle);
> -
> -extern int pnv_ocxl_spa_setup(struct pci_dev *dev, void *spa_mem, int PE_mask,
> - void **platform_data);
> -extern void pnv_ocxl_spa_release(void *platform_data);
> -extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle);
> -
> -extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
> -extern void pnv_ocxl_free_xive_irq(u32 irq);
> +int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap,
> + uint64_t rate_buf_phys, int rate_buf_size);
> +
> +int pnv_ocxl_get_xsl_irq(struct pci_dev *dev, int *hwirq);
> +void pnv_ocxl_unmap_xsl_regs(void __iomem *dsisr, void __iomem *dar,
> + void __iomem *tfc, void __iomem *pe_handle);
> +int pnv_ocxl_map_xsl_regs(struct pci_dev *dev, void __iomem **dsisr,
> + void __iomem **dar, void __iomem **tfc,
> + void __iomem **pe_handle);
> +
> +int pnv_ocxl_spa_setup(struct pci_dev *dev, void *spa_mem, int PE_mask, void **platform_data);
> +void pnv_ocxl_spa_release(void *platform_data);
> +int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle);
> +
> +int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
> +void pnv_ocxl_free_xive_irq(u32 irq);
> u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size);
> void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
>
> diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
> index 06dd5839e438..0a762e387418 100644
> --- a/include/misc/ocxl.h
> +++ b/include/misc/ocxl.h
> @@ -173,7 +173,7 @@ int ocxl_context_detach(struct ocxl_context *ctx);
> *
> * Returns 0 on success, negative on failure
> */
> -extern int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id);
> +int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id);
>
> /**
> * Frees an IRQ associated with an AFU context
> @@ -182,7 +182,7 @@ extern int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id);
> *
> * Returns 0 on success, negative on failure
> */
> -extern int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id);
> +int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id);
>
> /**
> * Gets the address of the trigger page for an IRQ
> @@ -193,7 +193,7 @@ extern int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id);
> *
> * returns the trigger page address, or 0 if the IRQ is not valid
> */
> -extern u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id);
> +u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id);
>
> /**
> * Provide a callback to be called when an IRQ is triggered
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 03/25] powerpc/powernv: Map & release OpenCAPI LPC memory
From: Dan Williams @ 2020-04-01 8:48 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-4-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> This patch adds OPAL calls to powernv so that the OpenCAPI
> driver can map & release LPC (Lowest Point of Coherency) memory.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
> ---
> arch/powerpc/include/asm/pnv-ocxl.h | 2 ++
> arch/powerpc/platforms/powernv/ocxl.c | 43 +++++++++++++++++++++++++++
> 2 files changed, 45 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
> index 7de82647e761..560a19bb71b7 100644
> --- a/arch/powerpc/include/asm/pnv-ocxl.h
> +++ b/arch/powerpc/include/asm/pnv-ocxl.h
> @@ -32,5 +32,7 @@ extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
>
> extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
> extern void pnv_ocxl_free_xive_irq(u32 irq);
> +u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size);
> +void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
>
> #endif /* _ASM_PNV_OCXL_H */
> diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c
> index 8c65aacda9c8..f13119a7c026 100644
> --- a/arch/powerpc/platforms/powernv/ocxl.c
> +++ b/arch/powerpc/platforms/powernv/ocxl.c
> @@ -475,6 +475,49 @@ void pnv_ocxl_spa_release(void *platform_data)
> }
> EXPORT_SYMBOL_GPL(pnv_ocxl_spa_release);
>
> +u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size)
> +{
> + struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> + struct pnv_phb *phb = hose->private_data;
Is calling the local variable 'hose' instead of 'host' on purpose?
> + u32 bdfn = pci_dev_id(pdev);
> + __be64 base_addr_be64;
> + u64 base_addr;
> + int rc;
> +
> + rc = opal_npu_mem_alloc(phb->opal_id, bdfn, size, &base_addr_be64);
> + if (rc) {
> + dev_warn(&pdev->dev,
> + "OPAL could not allocate LPC memory, rc=%d\n", rc);
> + return 0;
> + }
> +
> + base_addr = be64_to_cpu(base_addr_be64);
> +
> +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
With the proposed cleanup in patch2 the ifdef can be elided here.
> + rc = check_hotplug_memory_addressable(base_addr >> PAGE_SHIFT,
> + size >> PAGE_SHIFT);
> + if (rc)
> + return 0;
Is this an error worth logging if someone is wondering why their
device is not showing up?
> +#endif
> +
> + return base_addr;
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_setup);
> +
> +void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev)
> +{
> + struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> + struct pnv_phb *phb = hose->private_data;
> + u32 bdfn = pci_dev_id(pdev);
> + int rc;
> +
> + rc = opal_npu_mem_release(phb->opal_id, bdfn);
> + if (rc)
> + dev_warn(&pdev->dev,
> + "OPAL reported rc=%d when releasing LPC memory\n", rc);
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_release);
> +
> int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
> {
> struct spa_data *data = (struct spa_data *) platform_data;
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 02/25] mm/memory_hotplug: Allow check_hotplug_memory_addressable to be called from drivers
From: Dan Williams @ 2020-04-01 8:48 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-3-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> When setting up OpenCAPI connected persistent memory, the range check may
> not be performed until quite late (or perhaps not at all, if the user does
> not establish a DAX device).
>
> This patch makes the range check callable so we can perform the check while
> probing the OpenCAPI Persistent Memory device.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
> ---
> include/linux/memory_hotplug.h | 5 +++++
> mm/memory_hotplug.c | 4 ++--
> 2 files changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index f4d59155f3d4..9a19ae0d7e31 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -337,6 +337,11 @@ static inline void __remove_memory(int nid, u64 start, u64 size) {}
> extern void set_zone_contiguous(struct zone *zone);
> extern void clear_zone_contiguous(struct zone *zone);
>
> +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
> +int check_hotplug_memory_addressable(unsigned long pfn,
> + unsigned long nr_pages);
> +#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
Let's move this to include/linux/memory.h with the other
CONFIG_MEMORY_HOTPLUG_SPARSE declarations, and add a dummy
implementation for the CONFIG_MEMORY_HOTPLUG_SPARSE=n case.
Also, this patch can be squashed with the next one, no need for it to
be stand alone.
> +
> extern void __ref free_area_init_core_hotplug(int nid);
> extern int __add_memory(int nid, u64 start, u64 size);
> extern int add_memory(int nid, u64 start, u64 size);
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 0a54ffac8c68..14945f033594 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -276,8 +276,8 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
> return 0;
> }
>
> -static int check_hotplug_memory_addressable(unsigned long pfn,
> - unsigned long nr_pages)
> +int check_hotplug_memory_addressable(unsigned long pfn,
> + unsigned long nr_pages)
> {
> const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
>
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 01/25] powerpc/powernv: Add OPAL calls for LPC memory alloc/release
From: Dan Williams @ 2020-04-01 8:48 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-2-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> Add OPAL calls for LPC memory alloc/release
>
This seems to be referencing an existing api definition, can you
include a pointer to the spec in case someone wanted to understand
what these routines do? I suspect this is not allocating memory in the
traditional sense as much as it's allocating physical address space
for a device to be mapped?
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
> Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>
> ---
> arch/powerpc/include/asm/opal-api.h | 2 ++
> arch/powerpc/include/asm/opal.h | 2 ++
> arch/powerpc/platforms/powernv/opal-call.c | 2 ++
> 3 files changed, 6 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
> index c1f25a760eb1..9298e603001b 100644
> --- a/arch/powerpc/include/asm/opal-api.h
> +++ b/arch/powerpc/include/asm/opal-api.h
> @@ -208,6 +208,8 @@
> #define OPAL_HANDLE_HMI2 166
> #define OPAL_NX_COPROC_INIT 167
> #define OPAL_XIVE_GET_VP_STATE 170
> +#define OPAL_NPU_MEM_ALLOC 171
> +#define OPAL_NPU_MEM_RELEASE 172
> #define OPAL_MPIPL_UPDATE 173
> #define OPAL_MPIPL_REGISTER_TAG 174
> #define OPAL_MPIPL_QUERY_TAG 175
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index 9986ac34b8e2..301fea46c7ca 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -39,6 +39,8 @@ int64_t opal_npu_spa_clear_cache(uint64_t phb_id, uint32_t bdfn,
> uint64_t PE_handle);
> int64_t opal_npu_tl_set(uint64_t phb_id, uint32_t bdfn, long cap,
> uint64_t rate_phys, uint32_t size);
> +int64_t opal_npu_mem_alloc(u64 phb_id, u32 bdfn, u64 size, __be64 *bar);
> +int64_t opal_npu_mem_release(u64 phb_id, u32 bdfn);
>
> int64_t opal_console_write(int64_t term_number, __be64 *length,
> const uint8_t *buffer);
> diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
> index 5cd0f52d258f..f26e58b72c04 100644
> --- a/arch/powerpc/platforms/powernv/opal-call.c
> +++ b/arch/powerpc/platforms/powernv/opal-call.c
> @@ -287,6 +287,8 @@ OPAL_CALL(opal_pci_set_pbcq_tunnel_bar, OPAL_PCI_SET_PBCQ_TUNNEL_BAR);
> OPAL_CALL(opal_sensor_read_u64, OPAL_SENSOR_READ_U64);
> OPAL_CALL(opal_sensor_group_enable, OPAL_SENSOR_GROUP_ENABLE);
> OPAL_CALL(opal_nx_coproc_init, OPAL_NX_COPROC_INIT);
> +OPAL_CALL(opal_npu_mem_alloc, OPAL_NPU_MEM_ALLOC);
> +OPAL_CALL(opal_npu_mem_release, OPAL_NPU_MEM_RELEASE);
> OPAL_CALL(opal_mpipl_update, OPAL_MPIPL_UPDATE);
> OPAL_CALL(opal_mpipl_register_tag, OPAL_MPIPL_REGISTER_TAG);
> OPAL_CALL(opal_mpipl_query_tag, OPAL_MPIPL_QUERY_TAG);
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH v4 00/25] Add support for OpenCAPI Persistent Memory devices
From: Dan Williams @ 2020-04-01 8:47 UTC (permalink / raw)
To: Alastair D'Silva
Cc: Madhavan Srinivasan, Alexey Kardashevskiy, Masahiro Yamada,
Oliver O'Halloran, Mauro Carvalho Chehab, Ira Weiny,
Rob Herring, Dave Jiang, linux-nvdimm, Aneesh Kumar K . V,
Krzysztof Kozlowski, Anju T Sudhakar, Mahesh Salgaonkar,
Andrew Donnellan, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
Cédric Le Goater, Thomas Gleixner, Hari Bathini, Linux MM,
Greg Kroah-Hartman, Linux Kernel Mailing List, Vishal Verma,
Frederic Barrat, Paul Mackerras, Andrew Morton, linuxppc-dev,
David S. Miller
In-Reply-To: <20200327071202.2159885-1-alastair@d-silva.org>
On Sun, Mar 29, 2020 at 10:23 PM Alastair D'Silva <alastair@d-silva.org> wrote:
>
> This series adds support for OpenCAPI Persistent Memory devices on bare metal (arch/powernv), exposing them as nvdimms so that we can make use of the existing infrastructure. There already exists a driver for the same devices abstracted through PowerVM (arch/pseries): arch/powerpc/platforms/pseries/papr_scm.c
>
> These devices are connected via OpenCAPI, and present as LPC (lowest coherence point) memory to the system, practically, that means that memory on these cards could be treated as conventional, cache-coherent memory.
>
> Since the devices are connected via OpenCAPI, they are not enumerated via ACPI. Instead, OpenCAPI links present as pseudo-PCI bridges, with devices below them.
>
> This series introduces a driver that exposes the memory on these cards as nvdimms, with each card getting it's own bus. This is somewhat complicated by the fact that the cards do not have out of band persistent storage for metadata, so 1 SECTION_SIZE's (see SPARSEMEM) worth of storage is carved out of the top of the card storage to implement the ndctl_config_* calls.
Is it really tied to section-size? Can't that change based on the
configured page-size? It's not clear to me why that would be the
choice, but I'll dig into the implementation.
> The driver is not responsible for configuring the NPU (NVLink Processing Unit) BARs to map the LPC memory from the card into the system's physical address space, instead, it requests this to be done via OPAL calls (typically implemented by Skiboot).
Are OPAL calls similar to ACPI DSMs? I.e. methods for the OS to invoke
platform firmware services? What's Skiboot?
>
> The series is structured as follows:
> - Required infrastructure changes & cleanup
> - A minimal driver implementation
> - Implementing additional features within the driver
Thanks for the intro and the changelog!
>
> Changelog:
> V4:
> - Rebase on next-20200320
Do you have dependencies on other material that's in -next? Otherwise
-next is only a viable development baseline if you are going to merge
through Andrew's tree.
> - Bump copyright to 2020
> - Ensure all uapi headers use C89 compatible comments (missed ocxlpmem.h)
> - Move the driver back to drivers/nvdimm/ocxl, after confirmation
> that this location is desirable
> - Rename ocxl.c to ocxlpmem.c (+ support files)
> - Rename all ocxl_pmem to ocxlpmem
> - Address checkpatch --strict issues
> - "powerpc/powernv: Add OPAL calls for LPC memory alloc/release"
> - Pass base address as __be64
> - "ocxl: Tally up the LPC memory on a link & allow it to be mapped"
> - Address checkpatch spacing warnings
> - Reword blurb
> - Reword size description for ocxl_link_add_lpc_mem()
> - Add an early exit in ocxl_link_lpc_release() to avoid triggering
> bogus warnings if called after ocxl_link_lpc_map() fails
> - "powerpc/powernv: Add OPAL calls for LPC memory alloc/release"
> - Reword blurb
> - "powerpc/powernv: Map & release OpenCAPI LPC memory"
> - Reword blurb
> - Move minor_idr init from file_init() to ocxlpmem_init() (fixes runtime error
> in "nvdimm: Add driver for OpenCAPI Persistent Memory")
> - Wrap long lines
> - "nvdimm: Add driver for OpenCAPI Storage Class Memory"
> - Remove '+ 1' workround from serial number->cookie assignment
> - Drop out of memory message for ocxlpmem in probe()
> - Fix leaks of ocxlpmem & ocxlpmem->ocxl_fn in probe()
> - remove struct ocxlpmem_function0, it didn't value add
> - factor out err_unregistered label in probe
> - Address more checkpatch warnings
> - get/put the pci dev on probe/free
> - Drop ocxlpmem_ prefix from static functions
> - Propogate errors up from called functions in probe()
> - Set MODULE_LICENSE to GPLv2
> - Add myself as module author
> - Call nvdimm_bus_unregister() in remove() to release references
> - Don't call devm_memunmap on metadata_address, the release handler on
> the device already deals with this
> - "nvdimm/ocxl: Read the capability registers & wait for device ready"
> - Fix mask for read_latency
> - Fold in is_usable logic into timeout to remove error message race
> - propogate bad rc from read_device_metadata
> - "nvdimm/ocxl: Add register addresses & status values to the header"
> - Add comments for register abbreviations where names have been
> expanded
> - Add missing status for blocked on background task
> - Alias defines for firmware update status to show that the duplication
> of values is intentional
> - "nvdimm/ocxl: Register a character device for userspace to interact with"
> - Add lock around minors IDR, delete the cdev before device_unregister
> - Propogate errors up from called functions in probe()
> - "nvdimm/ocxl: Add support for Admin commands"
> - Fix typo in setup_command_data error message, and drop 'ocxl' from it
> - Drop vestigial CHI read from admin_command_request
> - Change command ID mismatch message to dev_err, and return an error
> - Use jiffies to implement admin_command_complete_timeout()
> - Flesh out blurb
> - Create a wrapper to issue the command & wait for timeout
> - "nvdimm/ocxl: Add support for near storage commands"
> - dropped (will submit with the patches for nvdimm overwrite)
> - "nvdimm/ocxl: Implement the Read Error Log command"
> - Remove stray blank line
> - change misplaced goto to an early exit in read_error_log
> - Inline error_log_offset_0x08
> - Read WWID data as LE rather than host endian
> - Move the include of nvdimm/ocxlpmem.h to ocxl.c
> - Add padding after fwrevision in struct ioctl_ocxl_pmem_error_log
> - Register IOCTL magic
> - Coerce pointers to __u64 in IOCTLs
> - "nvdimm/ocxl: Add controller dump IOCTLs"
> - Coerce pointers to __u64 in IOCTLs
> - Document expected IOCTL usage in blurb
> - Add missing rc check
> - Only populate up to the number of bytes returned by the card,
> and return this length to the caller
> - Add missing header check
> - "nvdimm/ocxl: Add an IOCTL to report controller statistics"
> - Update to match the latest version of the spec
> - Verify that parametr block IDs & lengths match what we expect
> - Use defines for offsets
> - "nvdimm/ocxl: Forward events to userspace"
> - Don't enable NSCRA doorbell
> - return -EBUSY if the event context is already used
> - return -ENODEV if IRQs cannot be mapped
> - Tag IRQ pointers with __iomem
> - Drop ocxlpmem_ prefix from static functions
> - Propogate error from eventfd_ctx_fdget
> - Fix error check in copy_to_user
> - Drop GLOBAL_MMIO_CHI_NSCRA (this should be in the overwrite patch)
> - Drop unused irq_pgmap
> - Don't redef BIT_ULL
> - "nvdimm/ocxl: Add debug IOCTLs"
> - Eliminate clearing loop (now done in admin_command_execute()
> - Drop dummy IOCTLs if CONFIG_OCXL_PMEM_DEBUG is not set
> - Group debug IOCTLs together & comment that they may not be available
> - "nvdimm/ocxl: Expose SMART data via ndctl"
> - Drop 'rc = 0; goto out;'
> - Propogate errors from ndctl_smart()
> - "nvdimm/ocxl: Expose the serial number in sysfs" & "nvdimm/ocxl: Expose the firmware version in sysfs"
> - Squash these 2 patches together
> - Expose data as a DIMM attribute rather than an ocxlpmem
> attribute
> - "nvdimm/ocxl: Add an IOCTL to request controller health & perf data"
> - Reword blurb
> - "nvdimm/ocxl: Implement the heartbeat command"
> - Propogate rc in probe()
>
> V3:
> - Rebase against next/next-20200220
> - Move driver to arch/powerpc/platforms/powernv, we now expect this
> driver to go upstream via the powerpc tree
> - "nvdimm/ocxl: Implement the Read Error Log command"
> - Fix bad header path
> - "nvdimm/ocxl: Read the capability registers & wait for device ready"
> - Fix overlapping masks between readiness_timeout & memory_available_timeout
> - "nvdimm: Add driver for OpenCAPI Storage Class Memory"
> - Address minor review comments from Jonathan Cameron
> - Remove attributes
> - Default to module if building LIBNVDIMM
> - Propogate errors up from called functions in probe()
> - "nvdimm/ocxl: Expose SMART data via ndctl"
> - Pack attributes in struct
> - Support different size SMART buffers for compatibility with newer
> ndctls that may want more SMART attribs than we provide
> - Rework to to use ND_CMD_CALL instead of ND_CMD_SMART
> - drop "ocxl: Free detached contexts in ocxl_context_detach_all()"
> - "powerpc: Map & release OpenCAPI LPC memory"
> - Remove 'extern'
> - Only available with CONFIG_MEMORY_HOTPLUG_SPARSE
> - "ocxl: Tally up the LPC memory on a link & allow it to be mapped"
> - Address minor review comments from Jonathan Cameron
> - "ocxl: Add functions to map/unmap LPC memory"
> - Split detected memory message into a separate patch
> - Address minor review comments from Jonathan Cameron
> - Add a comment explaining why unmap_lpc_mem is in deconfigure_afu
> - "nvdimm/ocxl: Add support for Admin commands"
> - use sizeof(u64) rather than 0x08 when iterating u64s
> - "nvdimm/ocxl: Implement the heartbeat command"
> - Fix typo in blurb
> - Address kernel doc issues
> - Ensure all uapi headers use C89 compatible comments
> - Drop patches for firmware update & overwrite, these will be
> submitted later once patches are available for ndctl
> - Rename SCM to OpenCAPI Persistent Memory
>
> V2:
> - "powerpc: Map & release OpenCAPI LPC memory"
> - Fix #if -> #ifdef
> - use pci_dev_id to get the bdfn
> - use __be64 to hold be data
> - indent check_hotplug_memory_addressable correctly
> - Remove export of check_hotplug_memory_addressable
> - "ocxl: Conditionally bind SCM devices to the generic OCXL driver"
> - Improve patch description and remove redundant default
> - "nvdimm: Add driver for OpenCAPI Storage Class Memory"
> - Mark a few funcs as static as identified by the 0day bot
> - Add OCXL dependancies to OCXL_SCM
> - Use memcpy_mcsafe in scm_ndctl_config_read
> - Rename scm_foo_offset_0x00 to scm_foo_header_parse & add docs
> - Name DIMM attribs "ocxl" rather than "scm"
> - Split out into base + many feature patches
> - "powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal"
> - Build DEV_DAX & friends as modules
> - "ocxl: Conditionally bind SCM devices to the generic OCXL driver"
> - Patch dropped (easy enough to maintain this out of tree for development)
> - "ocxl: Tally up the LPC memory on a link & allow it to be mapped"
> - Add a warning if an unmatched lpc_release is called
> - "ocxl: Add functions to map/unmap LPC memory"
> - Use EXPORT_SYMBOL_GPL
>
>
> Alastair D'Silva (25):
> powerpc/powernv: Add OPAL calls for LPC memory alloc/release
> mm/memory_hotplug: Allow check_hotplug_memory_addressable to be called
> from drivers
> powerpc/powernv: Map & release OpenCAPI LPC memory
> ocxl: Remove unnecessary externs
> ocxl: Address kernel doc errors & warnings
> ocxl: Tally up the LPC memory on a link & allow it to be mapped
> ocxl: Add functions to map/unmap LPC memory
> ocxl: Emit a log message showing how much LPC memory was detected
> ocxl: Save the device serial number in ocxl_fn
> nvdimm: Add driver for OpenCAPI Persistent Memory
> powerpc: Enable the OpenCAPI Persistent Memory driver for
> powernv_defconfig
> nvdimm/ocxl: Add register addresses & status values to the header
> nvdimm/ocxl: Read the capability registers & wait for device ready
> nvdimm/ocxl: Add support for Admin commands
> nvdimm/ocxl: Register a character device for userspace to interact
> with
> nvdimm/ocxl: Implement the Read Error Log command
> nvdimm/ocxl: Add controller dump IOCTLs
> nvdimm/ocxl: Add an IOCTL to report controller statistics
> nvdimm/ocxl: Forward events to userspace
> nvdimm/ocxl: Add an IOCTL to request controller health & perf data
> nvdimm/ocxl: Implement the heartbeat command
> nvdimm/ocxl: Add debug IOCTLs
> nvdimm/ocxl: Expose SMART data via ndctl
> nvdimm/ocxl: Expose the serial number & firmware version in sysfs
> MAINTAINERS: Add myself & nvdimm/ocxl to ocxl
>
> .../userspace-api/ioctl/ioctl-number.rst | 1 +
> MAINTAINERS | 3 +
> arch/powerpc/configs/powernv_defconfig | 5 +
> arch/powerpc/include/asm/opal-api.h | 2 +
> arch/powerpc/include/asm/opal.h | 2 +
> arch/powerpc/include/asm/pnv-ocxl.h | 42 +-
> arch/powerpc/platforms/powernv/ocxl.c | 43 +
> arch/powerpc/platforms/powernv/opal-call.c | 2 +
> drivers/misc/ocxl/config.c | 74 +-
> drivers/misc/ocxl/core.c | 61 +
> drivers/misc/ocxl/link.c | 60 +
> drivers/misc/ocxl/ocxl_internal.h | 45 +-
> drivers/nvdimm/Kconfig | 2 +
> drivers/nvdimm/Makefile | 1 +
> drivers/nvdimm/ocxl/Kconfig | 21 +
> drivers/nvdimm/ocxl/Makefile | 7 +
> drivers/nvdimm/ocxl/main.c | 1975 +++++++++++++++++
> drivers/nvdimm/ocxl/ocxlpmem.h | 197 ++
> drivers/nvdimm/ocxl/ocxlpmem_internal.c | 280 +++
> include/linux/memory_hotplug.h | 5 +
> include/misc/ocxl.h | 122 +-
> include/uapi/linux/ndctl.h | 1 +
> include/uapi/nvdimm/ocxlpmem.h | 127 ++
> mm/memory_hotplug.c | 4 +-
> 24 files changed, 2983 insertions(+), 99 deletions(-)
> create mode 100644 drivers/nvdimm/ocxl/Kconfig
> create mode 100644 drivers/nvdimm/ocxl/Makefile
> create mode 100644 drivers/nvdimm/ocxl/main.c
> create mode 100644 drivers/nvdimm/ocxl/ocxlpmem.h
> create mode 100644 drivers/nvdimm/ocxl/ocxlpmem_internal.c
> create mode 100644 include/uapi/nvdimm/ocxlpmem.h
>
> --
> 2.24.1
>
^ permalink raw reply
* Re: [PATCH RFC] mm: remove CONFIG_HAVE_MEMBLOCK_NODE_MAP (was: Re: [PATCH v3 0/5] mm: Enable CONFIG_NODES_SPAN_OTHER_NODES by default for NUMA)
From: Mike Rapoport @ 2020-04-01 7:51 UTC (permalink / raw)
To: Baoquan He
Cc: mmorana, Catalin Marinas, Heiko Carstens, Michal Hocko,
open list:MEMORY MANAGEMENT, Paul Mackerras, H. Peter Anvin,
sparclinux, Alexander Duyck, linux-s390, x86,
Christian Borntraeger, Ingo Molnar, Hoan Tran, Pavel Tatashin,
lho, Vasily Gorbik, Vlastimil Babka, Will Deacon, Borislav Petkov,
Thomas Gleixner, linux-arm-kernel, Oscar Salvador, linux-kernel,
Andrew Morton, linuxppc-dev, David S. Miller
In-Reply-To: <20200401054227.GC2129@MiWiFi-R3L-srv>
Hi,
On Wed, Apr 01, 2020 at 01:42:27PM +0800, Baoquan He wrote:
> On 04/01/20 at 12:56am, Mike Rapoport wrote:
> > On Mon, Mar 30, 2020 at 11:58:43AM +0200, Michal Hocko wrote:
> > >
> > > What would it take to make ia64 use HAVE_MEMBLOCK_NODE_MAP? I would
> > > really love to see that thing go away. It is causing problems when
> > > people try to use memblock api.
> >
> > Well, it's a small patch in the end :)
> >
> > Currently all NUMA architectures currently enable
> > CONFIG_HAVE_MEMBLOCK_NODE_MAP and use free_area_init_nodes() to initialize
> > nodes and zones structures.
>
> I did some investigation, there are nine ARCHes having NUMA config. And
> among them, alpha doesn't have HAVE_MEMBLOCK_NODE_MAP support.
alpha has NUMA marked as BROKEN for a long time now. I doubt anybody is
going to fix that.
> While the interesting thing is there are two ARCHes which have
> HAVE_MEMBLOCK_NODE_MAP config, but don't have NUMA config adding, they
> are microblaze and riscv. Obviously it was not carefully considered to
> add HAVE_MEMBLOCK_NODE_MAP config into riscv and microblaze.
Well, microblaze probably copied it and forgot to remove and riscv might
get actual NUMA at some point.
> arch/alpha/Kconfig:config NUMA
> arch/arm64/Kconfig:config NUMA
> arch/ia64/Kconfig:config NUMA
> arch/mips/Kconfig:config NUMA
> arch/powerpc/Kconfig:config NUMA
> arch/s390/Kconfig:config NUMA
> arch/sh/mm/Kconfig:config NUMA
> arch/sparc/Kconfig:config NUMA
> arch/x86/Kconfig:config NUMA
>
> From above information, we can remove HAVE_MEMBLOCK_NODE_MAP, and
> replace it with CONFIG_NUMA. That sounds more sensible to store nid into
> memblock when NUMA support is enabled.
Replacing CONFIG_HAVE_MEMBLOCK_NODE_MAP with CONFIG_NUMA will work, but
this will not help cleaning up the whole node/zone initialization mess and
we'll be stuck with two implementations.
The overhead of enabling HAVE_MEMBLOCK_NODE_MAP is only for init time as
most architectures will anyway discard the entire memblock, so having it in
a UMA arch won't be a problem. The only exception is arm that uses
memblock for pfn_valid(), here we may also think about a solution to
compensate the addition of nid to the memblock structures.
We also may want to make all the movable_node and movable_core
functionality dependent on CONFIG_NUMA.
> > diff --git a/include/linux/memblock.h b/include/linux/memblock.h
> > index 079d17d96410..9de81112447e 100644
> > --- a/include/linux/memblock.h
> > +++ b/include/linux/memblock.h
> > @@ -50,9 +50,7 @@ struct memblock_region {
> > phys_addr_t base;
> > phys_addr_t size;
> > enum memblock_flags flags;
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > int nid;
> > -#endif
>
> I didn't look into other change very carefully, but feel enabling
> memblock node map for all ARCHes looks a little radical. After all, many
> ARCHes even don't have NUMA support.
>
> > };
> >
> > /**
> > @@ -215,7 +213,6 @@ static inline bool memblock_is_nomap(struct memblock_region *m)
> > return m->flags & MEMBLOCK_NOMAP;
> > }
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
> > unsigned long *end_pfn);
> > void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
> > @@ -234,7 +231,6 @@ void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
> > #define for_each_mem_pfn_range(i, nid, p_start, p_end, p_nid) \
> > for (i = -1, __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid); \
> > i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid))
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> >
> > #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
> > void __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,
> > @@ -310,7 +306,6 @@ void __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,
> > for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \
> > nid, flags, p_start, p_end, p_nid)
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > int memblock_set_node(phys_addr_t base, phys_addr_t size,
> > struct memblock_type *type, int nid);
> >
> > @@ -323,16 +318,6 @@ static inline int memblock_get_region_node(const struct memblock_region *r)
> > {
> > return r->nid;
> > }
> > -#else
> > -static inline void memblock_set_region_node(struct memblock_region *r, int nid)
> > -{
> > -}
> > -
> > -static inline int memblock_get_region_node(const struct memblock_region *r)
> > -{
> > - return 0;
> > -}
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> >
> > /* Flags for memblock allocation APIs */
> > #define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0)
> > diff --git a/include/linux/mm.h b/include/linux/mm.h
> > index c54fb96cb1e6..368a45d4696a 100644
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -2125,9 +2125,8 @@ static inline unsigned long get_num_physpages(void)
> > return phys_pages;
> > }
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > /*
> > - * With CONFIG_HAVE_MEMBLOCK_NODE_MAP set, an architecture may initialise its
> > + * Using memblock node mappings, an architecture may initialise its
> > * zones, allocate the backing mem_map and account for memory holes in a more
> > * architecture independent manner. This is a substitute for creating the
> > * zone_sizes[] and zholes_size[] arrays and passing them to
> > @@ -2148,9 +2147,6 @@ static inline unsigned long get_num_physpages(void)
> > * registered physical page range. Similarly
> > * sparse_memory_present_with_active_regions() calls memory_present() for
> > * each range when SPARSEMEM is enabled.
> > - *
> > - * See mm/page_alloc.c for more information on each function exposed by
> > - * CONFIG_HAVE_MEMBLOCK_NODE_MAP.
> > */
> > extern void free_area_init_nodes(unsigned long *max_zone_pfn);
> > unsigned long node_map_pfn_alignment(void);
> > @@ -2165,22 +2161,12 @@ extern void free_bootmem_with_active_regions(int nid,
> > unsigned long max_low_pfn);
> > extern void sparse_memory_present_with_active_regions(int nid);
> >
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> >
> > -#if !defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) && \
> > - !defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID)
> > -static inline int __early_pfn_to_nid(unsigned long pfn,
> > - struct mminit_pfnnid_cache *state)
> > -{
> > - return 0;
> > -}
> > -#else
> > /* please see mm/page_alloc.c */
> > extern int __meminit early_pfn_to_nid(unsigned long pfn);
> > /* there is a per-arch backend function. */
> > extern int __meminit __early_pfn_to_nid(unsigned long pfn,
> > struct mminit_pfnnid_cache *state);
> > -#endif
> >
> > extern void set_dma_reserve(unsigned long new_dma_reserve);
> > extern void memmap_init_zone(unsigned long, int, unsigned long, unsigned long,
> > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> > index 462f6873905a..4422d1961d0e 100644
> > --- a/include/linux/mmzone.h
> > +++ b/include/linux/mmzone.h
> > @@ -917,11 +917,7 @@ extern int movable_zone;
> > #ifdef CONFIG_HIGHMEM
> > static inline int zone_movable_is_highmem(void)
> > {
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > return movable_zone == ZONE_HIGHMEM;
> > -#else
> > - return (ZONE_MOVABLE - 1) == ZONE_HIGHMEM;
> > -#endif
> > }
> > #endif
> >
> > @@ -1121,15 +1117,6 @@ static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist,
> > #include <asm/sparsemem.h>
> > #endif
> >
> > -#if !defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID) && \
> > - !defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
> > -static inline unsigned long early_pfn_to_nid(unsigned long pfn)
> > -{
> > - BUILD_BUG_ON(IS_ENABLED(CONFIG_NUMA));
> > - return 0;
> > -}
> > -#endif
> > -
> > #ifdef CONFIG_FLATMEM
> > #define pfn_to_nid(pfn) (0)
> > #endif
> > diff --git a/mm/memblock.c b/mm/memblock.c
> > index eba94ee3de0b..819441133a21 100644
> > --- a/mm/memblock.c
> > +++ b/mm/memblock.c
> > @@ -620,9 +620,7 @@ static int __init_memblock memblock_add_range(struct memblock_type *type,
> > * area, insert that portion.
> > */
> > if (rbase > base) {
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > WARN_ON(nid != memblock_get_region_node(rgn));
> > -#endif
> > WARN_ON(flags != rgn->flags);
> > nr_new++;
> > if (insert)
> > @@ -1197,7 +1195,6 @@ void __init_memblock __next_mem_range_rev(u64 *idx, int nid,
> > *idx = ULLONG_MAX;
> > }
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > /*
> > * Common iterator interface used to define for_each_mem_pfn_range().
> > */
> > @@ -1258,7 +1255,7 @@ int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size,
> > memblock_merge_regions(type);
> > return 0;
> > }
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> > +
> > #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
> > /**
> > * __next_mem_pfn_range_in_zone - iterator for for_each_*_range_in_zone()
> > @@ -1797,7 +1794,6 @@ bool __init_memblock memblock_is_map_memory(phys_addr_t addr)
> > return !memblock_is_nomap(&memblock.memory.regions[i]);
> > }
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > int __init_memblock memblock_search_pfn_nid(unsigned long pfn,
> > unsigned long *start_pfn, unsigned long *end_pfn)
> > {
> > @@ -1812,7 +1808,6 @@ int __init_memblock memblock_search_pfn_nid(unsigned long pfn,
> >
> > return type->regions[mid].nid;
> > }
> > -#endif
> >
> > /**
> > * memblock_is_region_memory - check if a region is a subset of memory
> > @@ -1903,11 +1898,9 @@ static void __init_memblock memblock_dump(struct memblock_type *type)
> > size = rgn->size;
> > end = base + size - 1;
> > flags = rgn->flags;
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > if (memblock_get_region_node(rgn) != MAX_NUMNODES)
> > snprintf(nid_buf, sizeof(nid_buf), " on node %d",
> > memblock_get_region_node(rgn));
> > -#endif
> > pr_info(" %s[%#x]\t[%pa-%pa], %pa bytes%s flags: %#x\n",
> > type->name, idx, &base, &end, &size, nid_buf, flags);
> > }
> > diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> > index 19389cdc16a5..dc8828b087bf 100644
> > --- a/mm/memory_hotplug.c
> > +++ b/mm/memory_hotplug.c
> > @@ -1366,11 +1366,7 @@ check_pages_isolated_cb(unsigned long start_pfn, unsigned long nr_pages,
> >
> > static int __init cmdline_parse_movable_node(char *p)
> > {
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > movable_node_enabled = true;
> > -#else
> > - pr_warn("movable_node parameter depends on CONFIG_HAVE_MEMBLOCK_NODE_MAP to work properly\n");
> > -#endif
> > return 0;
> > }
> > early_param("movable_node", cmdline_parse_movable_node);
> > diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> > index 3c4eb750a199..84a69d6e7e61 100644
> > --- a/mm/page_alloc.c
> > +++ b/mm/page_alloc.c
> > @@ -335,7 +335,6 @@ static unsigned long nr_kernel_pages __initdata;
> > static unsigned long nr_all_pages __initdata;
> > static unsigned long dma_reserve __initdata;
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > static unsigned long arch_zone_lowest_possible_pfn[MAX_NR_ZONES] __initdata;
> > static unsigned long arch_zone_highest_possible_pfn[MAX_NR_ZONES] __initdata;
> > static unsigned long required_kernelcore __initdata;
> > @@ -348,7 +347,6 @@ static bool mirrored_kernelcore __meminitdata;
> > /* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
> > int movable_zone;
> > EXPORT_SYMBOL(movable_zone);
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> >
> > #if MAX_NUMNODES > 1
> > unsigned int nr_node_ids __read_mostly = MAX_NUMNODES;
> > @@ -1447,9 +1445,6 @@ void __free_pages_core(struct page *page, unsigned int order)
> > __free_pages(page, order);
> > }
> >
> > -#if defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID) || \
> > - defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
> > -
> > static struct mminit_pfnnid_cache early_pfnnid_cache __meminitdata;
> >
> > int __meminit early_pfn_to_nid(unsigned long pfn)
> > @@ -1465,7 +1460,6 @@ int __meminit early_pfn_to_nid(unsigned long pfn)
> >
> > return nid;
> > }
> > -#endif
> >
> > #ifdef CONFIG_NODES_SPAN_OTHER_NODES
> > /* Only safe to use early in boot when initialisation is single-threaded */
> > @@ -5828,7 +5822,6 @@ void __ref build_all_zonelists(pg_data_t *pgdat)
> > static bool __meminit
> > overlap_memmap_init(unsigned long zone, unsigned long *pfn)
> > {
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > static struct memblock_region *r;
> >
> > if (mirrored_kernelcore && zone == ZONE_MOVABLE) {
> > @@ -5844,7 +5837,7 @@ overlap_memmap_init(unsigned long zone, unsigned long *pfn)
> > return true;
> > }
> > }
> > -#endif
> > +
> > return false;
> > }
> >
> > @@ -6227,7 +6220,6 @@ void __meminit init_currently_empty_zone(struct zone *zone,
> > zone->initialized = 1;
> > }
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > #ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID
> >
> > /*
> > @@ -6503,8 +6495,7 @@ static unsigned long __init zone_absent_pages_in_node(int nid,
> > return nr_absent;
> > }
> >
> > -#else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> > -static inline unsigned long __init zone_spanned_pages_in_node(int nid,
> > +static inline unsigned long __init compat_zone_spanned_pages_in_node(int nid,
> > unsigned long zone_type,
> > unsigned long node_start_pfn,
> > unsigned long node_end_pfn,
> > @@ -6523,7 +6514,7 @@ static inline unsigned long __init zone_spanned_pages_in_node(int nid,
> > return zones_size[zone_type];
> > }
> >
> > -static inline unsigned long __init zone_absent_pages_in_node(int nid,
> > +static inline unsigned long __init compat_zone_absent_pages_in_node(int nid,
> > unsigned long zone_type,
> > unsigned long node_start_pfn,
> > unsigned long node_end_pfn,
> > @@ -6535,13 +6526,12 @@ static inline unsigned long __init zone_absent_pages_in_node(int nid,
> > return zholes_size[zone_type];
> > }
> >
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> > -
> > static void __init calculate_node_totalpages(struct pglist_data *pgdat,
> > unsigned long node_start_pfn,
> > unsigned long node_end_pfn,
> > unsigned long *zones_size,
> > - unsigned long *zholes_size)
> > + unsigned long *zholes_size,
> > + bool compat)
> > {
> > unsigned long realtotalpages = 0, totalpages = 0;
> > enum zone_type i;
> > @@ -6549,17 +6539,38 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat,
> > for (i = 0; i < MAX_NR_ZONES; i++) {
> > struct zone *zone = pgdat->node_zones + i;
> > unsigned long zone_start_pfn, zone_end_pfn;
> > + unsigned long spanned, absent;
> > unsigned long size, real_size;
> >
> > - size = zone_spanned_pages_in_node(pgdat->node_id, i,
> > - node_start_pfn,
> > - node_end_pfn,
> > - &zone_start_pfn,
> > - &zone_end_pfn,
> > - zones_size);
> > - real_size = size - zone_absent_pages_in_node(pgdat->node_id, i,
> > - node_start_pfn, node_end_pfn,
> > - zholes_size);
> > + if (compat) {
> > + spanned = compat_zone_spanned_pages_in_node(
> > + pgdat->node_id, i,
> > + node_start_pfn,
> > + node_end_pfn,
> > + &zone_start_pfn,
> > + &zone_end_pfn,
> > + zones_size);
> > + absent = compat_zone_absent_pages_in_node(
> > + pgdat->node_id, i,
> > + node_start_pfn,
> > + node_end_pfn,
> > + zholes_size);
> > + } else {
> > + spanned = zone_spanned_pages_in_node(pgdat->node_id, i,
> > + node_start_pfn,
> > + node_end_pfn,
> > + &zone_start_pfn,
> > + &zone_end_pfn,
> > + zones_size);
> > + absent = zone_absent_pages_in_node(pgdat->node_id, i,
> > + node_start_pfn,
> > + node_end_pfn,
> > + zholes_size);
> > + }
> > +
> > + size = spanned;
> > + real_size = size - absent;
> > +
> > if (size)
> > zone->zone_start_pfn = zone_start_pfn;
> > else
> > @@ -6859,10 +6870,8 @@ static void __ref alloc_node_mem_map(struct pglist_data *pgdat)
> > */
> > if (pgdat == NODE_DATA(0)) {
> > mem_map = NODE_DATA(0)->node_mem_map;
> > -#if defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) || defined(CONFIG_FLATMEM)
> > if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
> > mem_map -= offset;
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> > }
> > #endif
> > }
> > @@ -6879,9 +6888,10 @@ static inline void pgdat_set_deferred_range(pg_data_t *pgdat)
> > static inline void pgdat_set_deferred_range(pg_data_t *pgdat) {}
> > #endif
> >
> > -void __init free_area_init_node(int nid, unsigned long *zones_size,
> > - unsigned long node_start_pfn,
> > - unsigned long *zholes_size)
> > +void __init __free_area_init_node(int nid, unsigned long *zones_size,
> > + unsigned long node_start_pfn,
> > + unsigned long *zholes_size,
> > + bool compat)
> > {
> > pg_data_t *pgdat = NODE_DATA(nid);
> > unsigned long start_pfn = 0;
> > @@ -6893,16 +6903,17 @@ void __init free_area_init_node(int nid, unsigned long *zones_size,
> > pgdat->node_id = nid;
> > pgdat->node_start_pfn = node_start_pfn;
> > pgdat->per_cpu_nodestats = NULL;
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > - get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
> > - pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid,
> > - (u64)start_pfn << PAGE_SHIFT,
> > - end_pfn ? ((u64)end_pfn << PAGE_SHIFT) - 1 : 0);
> > -#else
> > - start_pfn = node_start_pfn;
> > -#endif
> > + if (!compat) {
> > + get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
> > + pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid,
> > + (u64)start_pfn << PAGE_SHIFT,
> > + end_pfn ? ((u64)end_pfn << PAGE_SHIFT) - 1 : 0);
> > + } else {
> > + start_pfn = node_start_pfn;
> > + }
> > +
> > calculate_node_totalpages(pgdat, start_pfn, end_pfn,
> > - zones_size, zholes_size);
> > + zones_size, zholes_size, compat);
> >
> > alloc_node_mem_map(pgdat);
> > pgdat_set_deferred_range(pgdat);
> > @@ -6910,6 +6921,14 @@ void __init free_area_init_node(int nid, unsigned long *zones_size,
> > free_area_init_core(pgdat);
> > }
> >
> > +void __init free_area_init_node(int nid, unsigned long *zones_size,
> > + unsigned long node_start_pfn,
> > + unsigned long *zholes_size)
> > +{
> > + __free_area_init_node(nid, zones_size, node_start_pfn, zholes_size,
> > + true);
> > +}
> > +
> > #if !defined(CONFIG_FLAT_NODE_MEM_MAP)
> > /*
> > * Initialize all valid struct pages in the range [spfn, epfn) and mark them
> > @@ -6993,8 +7012,6 @@ static inline void __init init_unavailable_mem(void)
> > }
> > #endif /* !CONFIG_FLAT_NODE_MEM_MAP */
> >
> > -#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
> > -
> > #if MAX_NUMNODES > 1
> > /*
> > * Figure out the number of possible node ids.
> > @@ -7423,8 +7440,8 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
> > init_unavailable_mem();
> > for_each_online_node(nid) {
> > pg_data_t *pgdat = NODE_DATA(nid);
> > - free_area_init_node(nid, NULL,
> > - find_min_pfn_for_node(nid), NULL);
> > + __free_area_init_node(nid, NULL,
> > + find_min_pfn_for_node(nid), NULL, false);
> >
> > /* Any memory on that node */
> > if (pgdat->node_present_pages)
> > @@ -7489,8 +7506,6 @@ static int __init cmdline_parse_movablecore(char *p)
> > early_param("kernelcore", cmdline_parse_kernelcore);
> > early_param("movablecore", cmdline_parse_movablecore);
> >
> > -#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
> > -
> > void adjust_managed_page_count(struct page *page, long count)
> > {
> > atomic_long_add(count, &page_zone(page)->managed_pages);
> > --
> > 2.25.1
> >
> >
>
--
Sincerely yours,
Mike.
^ permalink raw reply
* [PATCH] papr/scm: Add bad memory ranges to nvdimm bad ranges
From: Santosh Sivaraj @ 2020-04-01 7:47 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Ganesh Goudar, Oliver, Mahesh Salgaonkar, Aneesh Kumar K.V
Subscribe to the MCE notification and add the physical address which
generated a memory error to nvdimm bad range.
Signed-off-by: Santosh Sivaraj <santosh@fossix.org>
---
This patch depends on "powerpc/mce: Add MCE notification chain" [1].
Unlike the previous series[2], the patch adds badblock registration only for
pseries scm driver. Handling badblocks for baremetal (powernv) PMEM will be done
later and if possible get the badblock handling as a common code.
[1] https://lore.kernel.org/linuxppc-dev/20200330071219.12284-1-ganeshgr@linux.ibm.com/
[2] https://lore.kernel.org/linuxppc-dev/20190820023030.18232-1-santosh@fossix.org/
arch/powerpc/platforms/pseries/papr_scm.c | 96 ++++++++++++++++++++++-
1 file changed, 95 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index 0b4467e378e5..5012cbf4606e 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -12,6 +12,8 @@
#include <linux/libnvdimm.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/nd.h>
+#include <asm/mce.h>
#include <asm/plpar_wrappers.h>
@@ -39,8 +41,12 @@ struct papr_scm_priv {
struct resource res;
struct nd_region *region;
struct nd_interleave_set nd_set;
+ struct list_head region_list;
};
+LIST_HEAD(papr_nd_regions);
+DEFINE_MUTEX(papr_ndr_lock);
+
static int drc_pmem_bind(struct papr_scm_priv *p)
{
unsigned long ret[PLPAR_HCALL_BUFSIZE];
@@ -372,6 +378,10 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
dev_info(dev, "Region registered with target node %d and online node %d",
target_nid, online_nid);
+ mutex_lock(&papr_ndr_lock);
+ list_add_tail(&p->region_list, &papr_nd_regions);
+ mutex_unlock(&papr_ndr_lock);
+
return 0;
err: nvdimm_bus_unregister(p->bus);
@@ -379,6 +389,68 @@ err: nvdimm_bus_unregister(p->bus);
return -ENXIO;
}
+void papr_scm_add_badblock(struct nd_region *region, struct nvdimm_bus *bus,
+ u64 phys_addr)
+{
+ u64 aligned_addr = ALIGN_DOWN(phys_addr, L1_CACHE_BYTES);
+
+ if (nvdimm_bus_add_badrange(bus, aligned_addr, L1_CACHE_BYTES)) {
+ pr_err("Bad block registration for 0x%llx failed\n", phys_addr);
+ return;
+ }
+
+ pr_debug("Add memory range (0x%llx - 0x%llx) as bad range\n",
+ aligned_addr, aligned_addr + L1_CACHE_BYTES);
+
+ nvdimm_region_notify(region, NVDIMM_REVALIDATE_POISON);
+}
+
+static int handle_mce_ue(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct machine_check_event *evt = data;
+ struct papr_scm_priv *p;
+ u64 phys_addr;
+ bool found = false;
+
+ if (evt->error_type != MCE_ERROR_TYPE_UE)
+ return NOTIFY_DONE;
+
+ if (list_empty(&papr_nd_regions))
+ return NOTIFY_DONE;
+
+ phys_addr = evt->u.ue_error.physical_address +
+ (evt->u.ue_error.effective_address & ~PAGE_MASK);
+
+ if (!evt->u.ue_error.physical_address_provided ||
+ !is_zone_device_page(pfn_to_page(phys_addr >> PAGE_SHIFT)))
+ return NOTIFY_DONE;
+
+ /* mce notifier is called from a process context, so mutex is safe */
+ mutex_lock(&papr_ndr_lock);
+ list_for_each_entry(p, &papr_nd_regions, region_list) {
+ struct resource res = p->res;
+
+ if (phys_addr >= res.start && phys_addr <= res.end) {
+ found = true;
+ break;
+ }
+ }
+
+ mutex_unlock(&papr_ndr_lock);
+
+ if (!found)
+ return NOTIFY_DONE;
+
+ papr_scm_add_badblock(p->region, p->bus, phys_addr);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mce_ue_nb = {
+ .notifier_call = handle_mce_ue
+};
+
static int papr_scm_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
@@ -476,6 +548,10 @@ static int papr_scm_remove(struct platform_device *pdev)
{
struct papr_scm_priv *p = platform_get_drvdata(pdev);
+ mutex_lock(&papr_ndr_lock);
+ list_del(&(p->region_list));
+ mutex_unlock(&papr_ndr_lock);
+
nvdimm_bus_unregister(p->bus);
drc_pmem_unbind(p);
kfree(p->bus_desc.provider_name);
@@ -498,7 +574,25 @@ static struct platform_driver papr_scm_driver = {
},
};
-module_platform_driver(papr_scm_driver);
+static int __init papr_scm_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&papr_scm_driver);
+ if (!ret)
+ mce_register_notifier(&mce_ue_nb);
+
+ return ret;
+}
+module_init(papr_scm_init);
+
+static void __exit papr_scm_exit(void)
+{
+ mce_unregister_notifier(&mce_ue_nb);
+ platform_driver_unregister(&papr_scm_driver);
+}
+module_exit(papr_scm_exit);
+
MODULE_DEVICE_TABLE(of, papr_scm_match);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2 03/16] powerpc/watchpoint: Introduce function to get nr watchpoints dynamically
From: Christophe Leroy @ 2020-04-01 7:05 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: <b6ff59ce-8251-c50f-1c0e-b1f54fccb09f@linux.ibm.com>
Le 01/04/2020 à 08:50, Ravi Bangoria a écrit :
>
>
> On 4/1/20 11:59 AM, Christophe Leroy wrote:
>>
>>
>> Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
>>> So far we had only one watchpoint, so we have hardcoded HBP_NUM to 1.
>>> But future Power architecture is introducing 2nd DAWR and thus kernel
>>> should be able to dynamically find actual number of watchpoints
>>> supported by hw it's running on. Introduce function for the same.
>>> Also convert HBP_NUM macro to HBP_NUM_MAX, which will now represent
>>> maximum number of watchpoints supported by Powerpc.
>>
>>
>> Everywhere else in the code, it is called 'breakpoint', not 'watchpoint'.
>>
>> Wouldn't it be more consistent to call the function nr_bp_slots()
>> instead of nr_wp_slots() ?
>>
>> Especially as we are likely going to extend your changes to support
>> DABR2 in addition to DABR on BOOK3S/32.
>
> Sure. I don't have strong onion for nr_wp_slots() and I can rename it to
> nr_bp_slots(). but...
>
> Even though existing code uses breakpoint and watchpoint interchangeably,
> I'm using wp/watchpoint to represent data-breakpoint, to distinguish it
> from instruction-breakpoint (CIABR). So IMHO, nr_wp_slots() should return
> number DAWRs/DABRs and nr_bp_slots() should return number of CIABRs. Is
> that okay?
>
Yes that makes sense too. Up to you.
Christophe
^ permalink raw reply
* Re: [PATCH v2 06/16] powerpc/watchpoint: Provide DAWR number to __set_breakpoint
From: Christophe Leroy @ 2020-04-01 7:03 UTC (permalink / raw)
To: Ravi Bangoria, mpe, mikey
Cc: apopple, peterz, fweisbec, oleg, npiggin, linux-kernel, paulus,
jolsa, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <20200401061309.92442-7-ravi.bangoria@linux.ibm.com>
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.
Christophe
> bool ppc_breakpoint_available(void);
> #ifdef CONFIG_PPC_ADV_DEBUG_REGS
> extern void do_send_trap(struct pt_regs *regs, unsigned long address,
> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
> index 62549007c87a..4e4976c3248b 100644
> --- a/arch/powerpc/include/asm/hw_breakpoint.h
> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
> @@ -85,7 +85,7 @@ static inline void hw_breakpoint_disable(void)
> brk.len = 0;
> brk.hw_len = 0;
> if (ppc_breakpoint_available())
> - __set_breakpoint(&brk);
> + __set_breakpoint(&brk, 0);
> }
> extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
> int hw_breakpoint_handler(struct die_args *args);
> diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
> index 4120349e2abe..7562f9ceb77e 100644
> --- a/arch/powerpc/kernel/hw_breakpoint.c
> +++ b/arch/powerpc/kernel/hw_breakpoint.c
> @@ -63,7 +63,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
> * If so, DABR will be populated in single_step_dabr_instruction().
> */
> if (current->thread.last_hit_ubp != bp)
> - __set_breakpoint(info);
> + __set_breakpoint(info, 0);
>
> return 0;
> }
> @@ -221,7 +221,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
>
> info = counter_arch_bp(tsk->thread.last_hit_ubp);
> regs->msr &= ~MSR_SE;
> - __set_breakpoint(info);
> + __set_breakpoint(info, 0);
> tsk->thread.last_hit_ubp = NULL;
> }
>
> @@ -346,7 +346,7 @@ int hw_breakpoint_handler(struct die_args *args)
> if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
> perf_bp_event(bp, regs);
>
> - __set_breakpoint(info);
> + __set_breakpoint(info, 0);
> out:
> rcu_read_unlock();
> return rc;
> @@ -379,7 +379,7 @@ static int single_step_dabr_instruction(struct die_args *args)
> if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
> perf_bp_event(bp, regs);
>
> - __set_breakpoint(info);
> + __set_breakpoint(info, 0);
> current->thread.last_hit_ubp = NULL;
>
> /*
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index b548a584e465..e0275fcd0c55 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -630,7 +630,7 @@ void do_break (struct pt_regs *regs, unsigned long address,
> }
> #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
>
> -static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk);
> +static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk[HBP_NUM_MAX]);
>
> #ifdef CONFIG_PPC_ADV_DEBUG_REGS
> /*
> @@ -707,7 +707,7 @@ EXPORT_SYMBOL_GPL(switch_booke_debug_regs);
> static void set_breakpoint(struct arch_hw_breakpoint *brk)
> {
> preempt_disable();
> - __set_breakpoint(brk);
> + __set_breakpoint(brk, 0);
> preempt_enable();
> }
>
> @@ -793,13 +793,13 @@ static inline int set_breakpoint_8xx(struct arch_hw_breakpoint *brk)
> return 0;
> }
>
> -void __set_breakpoint(struct arch_hw_breakpoint *brk)
> +void __set_breakpoint(struct arch_hw_breakpoint *brk, int nr)
> {
> - memcpy(this_cpu_ptr(¤t_brk), brk, sizeof(*brk));
> + memcpy(this_cpu_ptr(¤t_brk[nr]), brk, sizeof(*brk));
>
> if (dawr_enabled())
> // Power8 or later
> - set_dawr(brk, 0);
> + set_dawr(brk, nr);
> else if (IS_ENABLED(CONFIG_PPC_8xx))
> set_breakpoint_8xx(brk);
> else if (!cpu_has_feature(CPU_FTR_ARCH_207S))
> @@ -1167,8 +1167,8 @@ struct task_struct *__switch_to(struct task_struct *prev,
> * schedule DABR
> */
> #ifndef CONFIG_HAVE_HW_BREAKPOINT
> - if (unlikely(!hw_brk_match(this_cpu_ptr(¤t_brk), &new->thread.hw_brk)))
> - __set_breakpoint(&new->thread.hw_brk);
> + if (unlikely(!hw_brk_match(this_cpu_ptr(¤t_brk[0]), &new->thread.hw_brk)))
> + __set_breakpoint(&new->thread.hw_brk, 0);
> #endif /* CONFIG_HAVE_HW_BREAKPOINT */
> #endif
>
> diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
> index d215f9554553..bbf237f072d4 100644
> --- a/arch/powerpc/kernel/signal.c
> +++ b/arch/powerpc/kernel/signal.c
> @@ -129,7 +129,7 @@ static void do_signal(struct task_struct *tsk)
> * triggered inside the kernel.
> */
> if (tsk->thread.hw_brk.address && tsk->thread.hw_brk.type)
> - __set_breakpoint(&tsk->thread.hw_brk);
> + __set_breakpoint(&tsk->thread.hw_brk, 0);
> #endif
> /* Re-enable the breakpoints for the signal stack */
> thread_change_pc(tsk, tsk->thread.regs);
> diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
> index 75ca41c04dd1..508d353e7f06 100644
> --- a/arch/powerpc/xmon/xmon.c
> +++ b/arch/powerpc/xmon/xmon.c
> @@ -935,7 +935,7 @@ static void insert_cpu_bpts(void)
> brk.address = dabr.address;
> brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
> brk.len = DABR_MAX_LEN;
> - __set_breakpoint(&brk);
> + __set_breakpoint(&brk, 0);
> }
>
> if (iabr)
>
^ permalink raw reply
* Re: [PATCH v2 05/16] powerpc/watchpoint: Provide DAWR number to set_dawr
From: Christophe Leroy @ 2020-04-01 7:03 UTC (permalink / raw)
To: Ravi Bangoria, mpe, mikey
Cc: apopple, peterz, fweisbec, oleg, npiggin, linux-kernel, paulus,
jolsa, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <20200401061309.92442-6-ravi.bangoria@linux.ibm.com>
Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
> Introduce new parameter 'nr' to set_dawr() which indicates which DAWR
> should be programed.
>
> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
> ---
> arch/powerpc/include/asm/hw_breakpoint.h | 4 ++--
> arch/powerpc/kernel/dawr.c | 15 ++++++++++-----
> arch/powerpc/kernel/process.c | 2 +-
> 3 files changed, 13 insertions(+), 8 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
> index 518b41eef924..62549007c87a 100644
> --- a/arch/powerpc/include/asm/hw_breakpoint.h
> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
> @@ -104,10 +104,10 @@ static inline bool dawr_enabled(void)
> {
> return dawr_force_enable;
> }
> -int set_dawr(struct arch_hw_breakpoint *brk);
> +int set_dawr(struct arch_hw_breakpoint *brk, int nr);
Wondering if it wouldn't make more sense to have nr as first argument.
Christophe
> #else
> static inline bool dawr_enabled(void) { return false; }
> -static inline int set_dawr(struct arch_hw_breakpoint *brk) { return -1; }
> +static inline int set_dawr(struct arch_hw_breakpoint *brk, int nr) { return -1; }
> #endif
>
> #endif /* __KERNEL__ */
> diff --git a/arch/powerpc/kernel/dawr.c b/arch/powerpc/kernel/dawr.c
> index e91b613bf137..311e51ee09f4 100644
> --- a/arch/powerpc/kernel/dawr.c
> +++ b/arch/powerpc/kernel/dawr.c
> @@ -16,7 +16,7 @@
> bool dawr_force_enable;
> EXPORT_SYMBOL_GPL(dawr_force_enable);
>
> -int set_dawr(struct arch_hw_breakpoint *brk)
> +int set_dawr(struct arch_hw_breakpoint *brk, int nr)
> {
> unsigned long dawr, dawrx, mrd;
>
> @@ -39,15 +39,20 @@ int set_dawr(struct arch_hw_breakpoint *brk)
> if (ppc_md.set_dawr)
> return ppc_md.set_dawr(dawr, dawrx);
>
> - mtspr(SPRN_DAWR0, dawr);
> - mtspr(SPRN_DAWRX0, dawrx);
> + if (nr == 0) {
> + mtspr(SPRN_DAWR0, dawr);
> + mtspr(SPRN_DAWRX0, dawrx);
> + } else {
> + mtspr(SPRN_DAWR1, dawr);
> + mtspr(SPRN_DAWRX1, dawrx);
> + }
>
> return 0;
> }
>
> static void set_dawr_cb(void *info)
> {
> - set_dawr(info);
> + set_dawr(info, 0);
> }
>
> static ssize_t dawr_write_file_bool(struct file *file,
> @@ -60,7 +65,7 @@ static ssize_t dawr_write_file_bool(struct file *file,
> /* Send error to user if they hypervisor won't allow us to write DAWR */
> if (!dawr_force_enable &&
> firmware_has_feature(FW_FEATURE_LPAR) &&
> - set_dawr(&null_brk) != H_SUCCESS)
> + set_dawr(&null_brk, 0) != H_SUCCESS)
> return -ENODEV;
>
> rc = debugfs_write_file_bool(file, user_buf, count, ppos);
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index 110db94cdf3c..b548a584e465 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -799,7 +799,7 @@ void __set_breakpoint(struct arch_hw_breakpoint *brk)
>
> if (dawr_enabled())
> // Power8 or later
> - set_dawr(brk);
> + set_dawr(brk, 0);
> else if (IS_ENABLED(CONFIG_PPC_8xx))
> set_breakpoint_8xx(brk);
> else if (!cpu_has_feature(CPU_FTR_ARCH_207S))
>
^ permalink raw reply
* Re: [PATCH v2 14/16] powerpc/watchpoint: Don't allow concurrent perf and ptrace events
From: Christophe Leroy @ 2020-04-01 6:52 UTC (permalink / raw)
To: Ravi Bangoria, mpe, mikey
Cc: apopple, peterz, fweisbec, oleg, npiggin, linux-kernel, paulus,
jolsa, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <20200401061309.92442-15-ravi.bangoria@linux.ibm.com>
Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
> With Book3s DAWR, ptrace and perf watchpoints on powerpc behaves
> differently. Ptrace watchpoint works in one-shot mode and generates
> signal before executing instruction. It's ptrace user's job to
> single-step the instruction and re-enable the watchpoint. OTOH, in
> case of perf watchpoint, kernel emulates/single-steps the instruction
> and then generates event. If perf and ptrace creates two events with
> same or overlapping address ranges, it's ambiguous to decide who
> should single-step the instruction. Because of this issue, don't
> allow perf and ptrace watchpoint at the same time if their address
> range overlaps.
>
> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
> ---
> arch/powerpc/include/asm/hw_breakpoint.h | 2 +
> arch/powerpc/kernel/hw_breakpoint.c | 222 +++++++++++++++++++++++
> kernel/events/hw_breakpoint.c | 16 ++
> 3 files changed, 240 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
> index abc4603c0efe..9d3bd1169591 100644
> --- a/arch/powerpc/include/asm/hw_breakpoint.h
> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
> @@ -70,6 +70,8 @@ extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
> unsigned long val, void *data);
> int arch_install_hw_breakpoint(struct perf_event *bp);
> void arch_uninstall_hw_breakpoint(struct perf_event *bp);
> +int arch_reserve_bp_slot(struct perf_event *bp);
> +void arch_release_bp_slot(struct perf_event *bp);
> void arch_unregister_hw_breakpoint(struct perf_event *bp);
> void hw_breakpoint_pmu_read(struct perf_event *bp);
> extern void flush_ptrace_hw_breakpoint(struct task_struct *tsk);
> diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
> index 07a6cdea84ed..f813acb0d9f0 100644
> --- a/arch/powerpc/kernel/hw_breakpoint.c
> +++ b/arch/powerpc/kernel/hw_breakpoint.c
> @@ -123,6 +123,228 @@ static bool is_ptrace_bp(struct perf_event *bp)
> return bp->overflow_handler == ptrace_triggered;
> }
>
> +struct breakpoint {
> + struct list_head list;
> + struct perf_event *bp;
> + bool ptrace_bp;
> +};
> +
> +static DEFINE_PER_CPU(struct breakpoint *, cpu_bps[HBP_NUM_MAX]);
> +static LIST_HEAD(task_bps);
> +
> +static struct breakpoint *alloc_breakpoint(struct perf_event *bp)
> +{
> + struct breakpoint *tmp;
> +
> + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
> + if (!tmp)
> + return ERR_PTR(-ENOMEM);
> + tmp->bp = bp;
> + tmp->ptrace_bp = is_ptrace_bp(bp);
> + return tmp;
> +}
> +
> +static bool bp_addr_range_overlap(struct perf_event *bp1, struct perf_event *bp2)
> +{
> + __u64 bp1_saddr, bp1_eaddr, bp2_saddr, bp2_eaddr;
> +
> + bp1_saddr = ALIGN_DOWN(bp1->attr.bp_addr, HW_BREAKPOINT_SIZE);
> + bp1_eaddr = ALIGN(bp1->attr.bp_addr + bp1->attr.bp_len, HW_BREAKPOINT_SIZE) - 1;
> + bp2_saddr = ALIGN_DOWN(bp2->attr.bp_addr, HW_BREAKPOINT_SIZE);
> + bp2_eaddr = ALIGN(bp2->attr.bp_addr + bp2->attr.bp_len, HW_BREAKPOINT_SIZE) - 1;
> +
> + return (bp1_saddr <= bp2_eaddr && bp1_eaddr >= bp2_saddr);
Could avoid the - 1 on bp1_eaddr and bp2_eaddr by doing:
return (bp1_saddr < bp2_eaddr && bp1_eaddr > bp2_saddr);
Christophe
^ permalink raw reply
* Re: [PATCH v2 03/16] powerpc/watchpoint: Introduce function to get nr watchpoints dynamically
From: Ravi Bangoria @ 2020-04-01 6:50 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: <7851b702-8a93-11a8-6553-e0a51967dfc0@c-s.fr>
On 4/1/20 11:59 AM, Christophe Leroy wrote:
>
>
> Le 01/04/2020 à 08:12, Ravi Bangoria a écrit :
>> So far we had only one watchpoint, so we have hardcoded HBP_NUM to 1.
>> But future Power architecture is introducing 2nd DAWR and thus kernel
>> should be able to dynamically find actual number of watchpoints
>> supported by hw it's running on. Introduce function for the same.
>> Also convert HBP_NUM macro to HBP_NUM_MAX, which will now represent
>> maximum number of watchpoints supported by Powerpc.
>
>
> Everywhere else in the code, it is called 'breakpoint', not 'watchpoint'.
>
> Wouldn't it be more consistent to call the function nr_bp_slots() instead of nr_wp_slots() ?
>
> Especially as we are likely going to extend your changes to support DABR2 in addition to DABR on BOOK3S/32.
Sure. I don't have strong onion for nr_wp_slots() and I can rename it to
nr_bp_slots(). but...
Even though existing code uses breakpoint and watchpoint interchangeably,
I'm using wp/watchpoint to represent data-breakpoint, to distinguish it
from instruction-breakpoint (CIABR). So IMHO, nr_wp_slots() should return
number DAWRs/DABRs and nr_bp_slots() should return number of CIABRs. Is
that okay?
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 6:50 UTC (permalink / raw)
To: Ravi Bangoria, mpe, mikey
Cc: apopple, peterz, fweisbec, oleg, npiggin, linux-kernel, paulus,
jolsa, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <20200401061309.92442-14-ravi.bangoria@linux.ibm.com>
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));
>
> +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));
Christophe
> +
> +static bool dar_hw_range_overlaps(unsigned long dar, int size,
> + 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 ((dar <= hw_end_addr) && (dar + size - 1 >= hw_start_addr));
> +}
Same
Christophe
^ 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