From 6e1e90ec027509a7e8d4efbd77a65b32b5a8b3ec Mon Sep 17 00:00:00 2001 From: Adrian Ratiu Date: Wed, 14 Oct 2020 23:30:24 +0300 Subject: [PATCH 001/127] regmap: mmio: add config option to allow relaxed MMIO accesses On some platforms (eg armv7 due to the CONFIG_ARM_DMA_MEM_BUFFERABLE) MMIO R/W operations always add memory barriers which can increase load, decrease battery life or in general reduce performance unnecessarily on devices which access a lot of configuration registers and where ordering does not matter (eg. media accelerators like the Verisilicon / Hantro video decoders). Drivers used to call the relaxed MMIO variants directly but since they are now accessing the MMIO registers via regmaps (to compensate for different VPU HW reg layouts via regmap fields), there is a need for a relaxed API / config to preserve existing behaviour. Cc: Mark Brown Signed-off-by: Adrian Ratiu Link: https://lore.kernel.org/r/20201014203024.954369-1-adrian.ratiu@collabora.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 90 ++++++++++++++++++++++++++++--- include/linux/regmap.h | 5 ++ 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index af967d8f975e..f9cd51afb9d2 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -16,6 +16,7 @@ struct regmap_mmio_context { void __iomem *regs; unsigned val_bytes; + bool relaxed_mmio; bool attached_clk; struct clk *clk; @@ -75,6 +76,13 @@ static void regmap_mmio_write8(struct regmap_mmio_context *ctx, writeb(val, ctx->regs + reg); } +static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg, + unsigned int val) +{ + writeb_relaxed(val, ctx->regs + reg); +} + static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, unsigned int reg, unsigned int val) @@ -82,6 +90,13 @@ static void regmap_mmio_write16le(struct regmap_mmio_context *ctx, writew(val, ctx->regs + reg); } +static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg, + unsigned int val) +{ + writew_relaxed(val, ctx->regs + reg); +} + static void regmap_mmio_write16be(struct regmap_mmio_context *ctx, unsigned int reg, unsigned int val) @@ -96,6 +111,13 @@ static void regmap_mmio_write32le(struct regmap_mmio_context *ctx, writel(val, ctx->regs + reg); } +static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg, + unsigned int val) +{ + writel_relaxed(val, ctx->regs + reg); +} + static void regmap_mmio_write32be(struct regmap_mmio_context *ctx, unsigned int reg, unsigned int val) @@ -110,6 +132,13 @@ static void regmap_mmio_write64le(struct regmap_mmio_context *ctx, { writeq(val, ctx->regs + reg); } + +static void regmap_mmio_write64le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg, + unsigned int val) +{ + writeq_relaxed(val, ctx->regs + reg); +} #endif static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val) @@ -137,12 +166,24 @@ static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx, return readb(ctx->regs + reg); } +static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg) +{ + return readb_relaxed(ctx->regs + reg); +} + static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx, unsigned int reg) { return readw(ctx->regs + reg); } +static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg) +{ + return readw_relaxed(ctx->regs + reg); +} + static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx, unsigned int reg) { @@ -155,6 +196,12 @@ static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx, return readl(ctx->regs + reg); } +static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg) +{ + return readl_relaxed(ctx->regs + reg); +} + static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx, unsigned int reg) { @@ -167,6 +214,12 @@ static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx, { return readq(ctx->regs + reg); } + +static unsigned int regmap_mmio_read64le_relaxed(struct regmap_mmio_context *ctx, + unsigned int reg) +{ + return readq_relaxed(ctx->regs + reg); +} #endif static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val) @@ -237,6 +290,7 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + ctx->relaxed_mmio = config->use_relaxed_mmio; ctx->clk = ERR_PTR(-ENODEV); switch (regmap_get_val_endian(dev, ®map_mmio, config)) { @@ -247,21 +301,41 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, #endif switch (config->val_bits) { case 8: - ctx->reg_read = regmap_mmio_read8; - ctx->reg_write = regmap_mmio_write8; + if (ctx->relaxed_mmio) { + ctx->reg_read = regmap_mmio_read8_relaxed; + ctx->reg_write = regmap_mmio_write8_relaxed; + } else { + ctx->reg_read = regmap_mmio_read8; + ctx->reg_write = regmap_mmio_write8; + } break; case 16: - ctx->reg_read = regmap_mmio_read16le; - ctx->reg_write = regmap_mmio_write16le; + if (ctx->relaxed_mmio) { + ctx->reg_read = regmap_mmio_read16le_relaxed; + ctx->reg_write = regmap_mmio_write16le_relaxed; + } else { + ctx->reg_read = regmap_mmio_read16le; + ctx->reg_write = regmap_mmio_write16le; + } break; case 32: - ctx->reg_read = regmap_mmio_read32le; - ctx->reg_write = regmap_mmio_write32le; + if (ctx->relaxed_mmio) { + ctx->reg_read = regmap_mmio_read32le_relaxed; + ctx->reg_write = regmap_mmio_write32le_relaxed; + } else { + ctx->reg_read = regmap_mmio_read32le; + ctx->reg_write = regmap_mmio_write32le; + } break; #ifdef CONFIG_64BIT case 64: - ctx->reg_read = regmap_mmio_read64le; - ctx->reg_write = regmap_mmio_write64le; + if (ctx->relaxed_mmio) { + ctx->reg_read = regmap_mmio_read64le_relaxed; + ctx->reg_write = regmap_mmio_write64le_relaxed; + } else { + ctx->reg_read = regmap_mmio_read64le; + ctx->reg_write = regmap_mmio_write64le; + } break; #endif default: diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e7834d98207f..126fe700d1d8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -315,6 +315,10 @@ typedef void (*regmap_unlock)(void *); * masks are used. * @zero_flag_mask: If set, read_flag_mask and write_flag_mask are used even * if they are both empty. + * @use_relaxed_mmio: If set, MMIO R/W operations will not use memory barriers. + * This can avoid load on devices which don't require strict + * orderings, but drivers should carefully add any explicit + * memory barriers when they may require them. * @use_single_read: If set, converts the bulk read operation into a series of * single read operations. This is useful for a device that * does not support bulk read. @@ -388,6 +392,7 @@ struct regmap_config { bool use_single_read; bool use_single_write; + bool use_relaxed_mmio; bool can_multi_write; enum regmap_endian reg_format_endian; From bf3a28cf42412c0a85631da94f198048bb37a8e5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 23 Oct 2020 16:19:22 +0300 Subject: [PATCH 002/127] regulator: fixed: support using power domain for enable/disable Adds possibility to choose the compatible "fixed-regulator-domain" for regulators which use power domain for enabling/disabling corresponding regulator. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20201023131925.334864-3-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 63 +++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 3de7709bdcd4..02ad83153e19 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,11 +36,13 @@ struct fixed_voltage_data { struct regulator_dev *dev; struct clk *enable_clock; - unsigned int clk_enable_counter; + unsigned int enable_counter; + int performance_state; }; struct fixed_dev_type { bool has_enable_clock; + bool has_performance_state; }; static int reg_clock_enable(struct regulator_dev *rdev) @@ -50,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev) if (ret) return ret; - priv->clk_enable_counter++; + priv->enable_counter++; return ret; } @@ -60,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev) struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); clk_disable_unprepare(priv->enable_clock); - priv->clk_enable_counter--; + priv->enable_counter--; return 0; } -static int reg_clock_is_enabled(struct regulator_dev *rdev) +static int reg_domain_enable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + int ret; + + ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state); + if (ret) + return ret; + + priv->enable_counter++; + + return ret; +} + +static int reg_domain_disable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + + priv->enable_counter--; + + return dev_pm_genpd_set_performance_state(dev, 0); +} + +static int reg_is_enabled(struct regulator_dev *rdev) { struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); - return priv->clk_enable_counter > 0; + return priv->enable_counter > 0; } @@ -129,7 +158,13 @@ static const struct regulator_ops fixed_voltage_ops = { static const struct regulator_ops fixed_voltage_clkenabled_ops = { .enable = reg_clock_enable, .disable = reg_clock_disable, - .is_enabled = reg_clock_is_enabled, + .is_enabled = reg_is_enabled, +}; + +static const struct regulator_ops fixed_voltage_domain_ops = { + .enable = reg_domain_enable, + .disable = reg_domain_disable, + .is_enabled = reg_is_enabled, }; static int reg_fixed_voltage_probe(struct platform_device *pdev) @@ -177,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) dev_err(dev, "Can't get enable-clock from devicetree\n"); return -ENOENT; } + } else if (drvtype && drvtype->has_performance_state) { + drvdata->desc.ops = &fixed_voltage_domain_ops; + + drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0); + if (drvdata->performance_state < 0) { + dev_err(dev, "Can't get performance state from devicetree\n"); + return drvdata->performance_state; + } } else { drvdata->desc.ops = &fixed_voltage_ops; } @@ -260,6 +303,10 @@ static const struct fixed_dev_type fixed_clkenable_data = { .has_enable_clock = true, }; +static const struct fixed_dev_type fixed_domain_data = { + .has_performance_state = true, +}; + static const struct of_device_id fixed_of_match[] = { { .compatible = "regulator-fixed", @@ -269,6 +316,10 @@ static const struct of_device_id fixed_of_match[] = { .compatible = "regulator-fixed-clock", .data = &fixed_clkenable_data, }, + { + .compatible = "regulator-fixed-domain", + .data = &fixed_domain_data, + }, { }, }; From d4189bc55d5c40251abaa1f341796aac84ddfb10 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 23 Oct 2020 16:19:21 +0300 Subject: [PATCH 003/127] regulator: fixed: provide bindings using power domain Define bindings for fixed regulator using power domain performance state to enable/disable corresponding regulator. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20201023131925.334864-2-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- .../bindings/regulator/fixed-regulator.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml b/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml index 92211f2b3b0c..d3d0dc13dd8b 100644 --- a/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml @@ -26,12 +26,22 @@ if: const: regulator-fixed-clock required: - clocks +else: + if: + properties: + compatible: + contains: + const: regulator-fixed-domain + required: + - power-domains + - required-opps properties: compatible: enum: - regulator-fixed - regulator-fixed-clock + - regulator-fixed-domain regulator-name: true @@ -46,6 +56,20 @@ properties: is mandatory if compatible is chosen to regulator-fixed-clock. maxItems: 1 + power-domains: + description: + Power domain to use for enable control. This binding is only + available if the compatible is chosen to regulator-fixed-domain. + maxItems: 1 + + required-opps: + description: + Performance state to use for enable control. This binding is only + available if the compatible is chosen to regulator-fixed-domain. The + power-domain binding is mandatory if compatible is chosen to + regulator-fixed-domain. + maxItems: 1 + startup-delay-us: description: startup time in microseconds $ref: /schemas/types.yaml#/definitions/uint32 @@ -89,4 +113,27 @@ examples: gpio-open-drain; vin-supply = <&parent_reg>; }; + reg_1v8_clk: regulator-1v8-clk { + compatible = "regulator-fixed-clock"; + regulator-name = "1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + clocks = <&clock1>; + startup-delay-us = <70000>; + enable-active-high; + regulator-boot-on; + vin-supply = <&parent_reg>; + }; + reg_1v8_domain: regulator-1v8-domain { + compatible = "regulator-fixed-domain"; + regulator-name = "1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + power-domains = <&domain1>; + required-opps = <&domain1_state1>; + startup-delay-us = <70000>; + enable-active-high; + regulator-boot-on; + vin-supply = <&parent_reg>; + }; ... From 390d828f56a602c9201601bff1170d9d2bf5801c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Oct 2020 23:22:35 +0100 Subject: [PATCH 004/127] regulator: lp872x: make a const array static, makes object smaller Don't populate const array lp872x_num_regulators on the stack but instead make it static. Makes the object code smaller by 29 bytes. Before: text data bss dec hex filename 18441 4624 64 23129 5a59 drivers/regulator/lp872x.o After: text data bss dec hex filename 18316 4720 64 23100 5a3c drivers/regulator/lp872x.o (gcc version 10.2.0) Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20201016222235.686981-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/regulator/lp872x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 303d7e2dc838..e84be29533f4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) struct lp872x *lp; struct lp872x_platform_data *pdata; int ret; - const int lp872x_num_regulators[] = { + static const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, }; From 345980a3a5e5e1c99fc621e2ce878fb150ad2287 Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Thu, 22 Oct 2020 10:52:21 +0300 Subject: [PATCH 005/127] spi: spi-sun6i: implement DMA-based transfer mode DMA-based transfer will be enabled if data length is larger than FIFO size (64 bytes for A64). This greatly reduce number of interrupts for transferring data. For smaller data size PIO mode will be used. In PIO mode whole buffer will be loaded into FIFO. If driver failed to request DMA channels then it fallback for PIO mode. Tested on SOPINE (https://www.pine64.org/sopine/) Signed-off-by: Alexander Kochetkov Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201022075221.23332-1-akochetkov@lintech.ru Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 194 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 176 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 19238e1b76b4..5336c5e32694 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -52,10 +53,12 @@ #define SUN6I_FIFO_CTL_REG 0x18 #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK 0xff +#define SUN6I_FIFO_CTL_RF_DRQ_EN BIT(8) #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS 0 #define SUN6I_FIFO_CTL_RF_RST BIT(15) #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK 0xff #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS 16 +#define SUN6I_FIFO_CTL_TF_DRQ_EN BIT(24) #define SUN6I_FIFO_CTL_TF_RST BIT(31) #define SUN6I_FIFO_STA_REG 0x1c @@ -83,6 +86,8 @@ struct sun6i_spi { struct spi_master *master; void __iomem *base_addr; + dma_addr_t dma_addr_rx; + dma_addr_t dma_addr_tx; struct clk *hclk; struct clk *mclk; struct reset_control *rstc; @@ -182,6 +187,68 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) return SUN6I_MAX_XFER_SIZE - 1; } +static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, + struct spi_transfer *tfr) +{ + struct dma_async_tx_descriptor *rxdesc, *txdesc; + struct spi_master *master = sspi->master; + + rxdesc = NULL; + if (tfr->rx_buf) { + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = sspi->dma_addr_rx, + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_maxburst = 8, + }; + + dmaengine_slave_config(master->dma_rx, &rxconf); + + rxdesc = dmaengine_prep_slave_sg(master->dma_rx, + tfr->rx_sg.sgl, + tfr->rx_sg.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!rxdesc) + return -EINVAL; + } + + txdesc = NULL; + if (tfr->tx_buf) { + struct dma_slave_config txconf = { + .direction = DMA_MEM_TO_DEV, + .dst_addr = sspi->dma_addr_tx, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_maxburst = 8, + }; + + dmaengine_slave_config(master->dma_tx, &txconf); + + txdesc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, + tfr->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!txdesc) { + if (rxdesc) + dmaengine_terminate_sync(master->dma_rx); + return -EINVAL; + } + } + + if (tfr->rx_buf) { + dmaengine_submit(rxdesc); + dma_async_issue_pending(master->dma_rx); + } + + if (tfr->tx_buf) { + dmaengine_submit(txdesc); + dma_async_issue_pending(master->dma_tx); + } + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -191,6 +258,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, unsigned int start, end, tx_time; unsigned int trig_level; unsigned int tx_len = 0, rx_len = 0; + bool use_dma; int ret = 0; u32 reg; @@ -201,6 +269,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; + use_dma = master->can_dma ? master->can_dma(master, spi, tfr) : false; /* Clear pending interrupts */ sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0); @@ -209,16 +278,34 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); - /* - * Setup FIFO interrupt trigger level - * Here we choose 3/4 of the full fifo depth, as it's the hardcoded - * value used in old generation of Allwinner SPI controller. - * (See spi-sun4i.c) - */ - trig_level = sspi->fifo_depth / 4 * 3; - sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); + reg = 0; + + if (!use_dma) { + /* + * Setup FIFO interrupt trigger level + * Here we choose 3/4 of the full fifo depth, as it's + * the hardcoded value used in old generation of Allwinner + * SPI controller. (See spi-sun4i.c) + */ + trig_level = sspi->fifo_depth / 4 * 3; + } else { + /* + * Setup FIFO DMA request trigger level + * We choose 1/2 of the full fifo depth, that value will + * be used as DMA burst length. + */ + trig_level = sspi->fifo_depth / 2; + + if (tfr->tx_buf) + reg |= SUN6I_FIFO_CTL_TF_DRQ_EN; + if (tfr->rx_buf) + reg |= SUN6I_FIFO_CTL_RF_DRQ_EN; + } + + reg |= (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | + (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS); + + sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, reg); /* * Setup the transfer control register: Chip Select, @@ -300,16 +387,28 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len); sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len); - /* Fill the TX FIFO */ - sun6i_spi_fill_fifo(sspi); + if (!use_dma) { + /* Fill the TX FIFO */ + sun6i_spi_fill_fifo(sspi); + } else { + ret = sun6i_spi_prepare_dma(sspi, tfr); + if (ret) { + dev_warn(&master->dev, + "%s: prepare DMA failed, ret=%d", + dev_name(&spi->dev), ret); + return ret; + } + } /* Enable the interrupts */ reg = SUN6I_INT_CTL_TC; - if (rx_len > sspi->fifo_depth) - reg |= SUN6I_INT_CTL_RF_RDY; - if (tx_len > sspi->fifo_depth) - reg |= SUN6I_INT_CTL_TF_ERQ; + if (!use_dma) { + if (rx_len > sspi->fifo_depth) + reg |= SUN6I_INT_CTL_RF_RDY; + if (tx_len > sspi->fifo_depth) + reg |= SUN6I_INT_CTL_TF_ERQ; + } sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); @@ -332,6 +431,11 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); + if (ret && use_dma) { + dmaengine_terminate_sync(master->dma_rx); + dmaengine_terminate_sync(master->dma_tx); + } + return ret; } @@ -422,10 +526,25 @@ static int sun6i_spi_runtime_suspend(struct device *dev) return 0; } +static bool sun6i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + /* + * If the number of spi words to transfer is less or equal than + * the fifo length we can just fill the fifo and wait for a single + * irq, so don't bother setting up dma + */ + return xfer->len > sspi->fifo_depth; +} + static int sun6i_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct sun6i_spi *sspi; + struct resource *mem; int ret = 0, irq; master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); @@ -437,7 +556,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); - sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); + sspi->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(sspi->base_addr)) { ret = PTR_ERR(sspi->base_addr); goto err_free_master; @@ -494,6 +613,33 @@ static int sun6i_spi_probe(struct platform_device *pdev) goto err_free_master; } + master->dma_tx = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR(master->dma_tx)) { + /* Check tx to see if we need defer probing driver */ + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_free_master; + } + dev_warn(&pdev->dev, "Failed to request TX DMA channel\n"); + master->dma_tx = NULL; + } + + master->dma_rx = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(master->dma_rx)) { + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_free_dma_tx; + } + dev_warn(&pdev->dev, "Failed to request RX DMA channel\n"); + master->dma_rx = NULL; + } + + if (master->dma_tx && master->dma_rx) { + sspi->dma_addr_tx = mem->start + SUN6I_TXDATA_REG; + sspi->dma_addr_rx = mem->start + SUN6I_RXDATA_REG; + master->can_dma = sun6i_spi_can_dma; + } + /* * This wake-up/shutdown pattern is to be able to have the * device woken up, even if runtime_pm is disabled @@ -501,7 +647,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) ret = sun6i_spi_runtime_resume(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Couldn't resume the device\n"); - goto err_free_master; + goto err_free_dma_rx; } pm_runtime_set_active(&pdev->dev); @@ -519,6 +665,12 @@ static int sun6i_spi_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); sun6i_spi_runtime_suspend(&pdev->dev); +err_free_dma_rx: + if (master->dma_rx) + dma_release_channel(master->dma_rx); +err_free_dma_tx: + if (master->dma_tx) + dma_release_channel(master->dma_tx); err_free_master: spi_master_put(master); return ret; @@ -526,8 +678,14 @@ err_free_master: static int sun6i_spi_remove(struct platform_device *pdev) { + struct spi_master *master = platform_get_drvdata(pdev); + pm_runtime_force_suspend(&pdev->dev); + if (master->dma_tx) + dma_release_channel(master->dma_tx); + if (master->dma_rx) + dma_release_channel(master->dma_rx); return 0; } From 940f3bbf3dacd6e31e482a10e64c96e69b00dded Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Fri, 16 Oct 2020 11:50:14 +0300 Subject: [PATCH 006/127] spi: rockchip: enable autosuspend feature If SPI is used for periodic polling any sensor, significant delays sometimes appear. Switching on module clocks during resume lead to delays. Enabling autosuspend mode causes the controller to not suspend between SPI transfers and the delays disappear. Signed-off-by: Alexander Kochetkov Link: https://lore.kernel.org/r/20201016085014.31667-1-al.kochet@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 75a8a9428ff8..09d8e92400eb 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -160,6 +160,8 @@ #define ROCKCHIP_SPI_VER2_TYPE1 0x05EC0002 #define ROCKCHIP_SPI_VER2_TYPE2 0x00110002 +#define ROCKCHIP_AUTOSUSPEND_TIMEOUT 2000 + struct rockchip_spi { struct device *dev; @@ -715,6 +717,8 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_disable_spiclk; } + pm_runtime_set_autosuspend_delay(&pdev->dev, ROCKCHIP_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); From ae0f18bef30d0e76dd62be46c59b24c75f175092 Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Mon, 19 Oct 2020 18:03:43 +0300 Subject: [PATCH 007/127] spi: spi-sun6i: enable autosuspend feature If SPI is used for periodic polling any sensor, significant delays sometimes appear. Switching on module clocks during resume lead to delays. Enabling autosuspend mode causes the controller to not suspend between SPI transfers and the delays disappear. The commit also remove unnecessary call to pm_runtime_idle() used to explicit put device to suspended state. Without pm_runtime_idle() PM core will put device in the suspended state just after probe() returns. Signed-off-by: Alexander Kochetkov Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201019150343.2520-1-akochetkov@lintech.ru Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 5336c5e32694..cc8401980125 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -22,6 +22,8 @@ #include +#define SUN6I_AUTOSUSPEND_TIMEOUT 2000 + #define SUN6I_FIFO_DEPTH 128 #define SUN8I_FIFO_DEPTH 64 @@ -650,9 +652,10 @@ static int sun6i_spi_probe(struct platform_device *pdev) goto err_free_dma_rx; } + pm_runtime_set_autosuspend_delay(&pdev->dev, SUN6I_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - pm_runtime_idle(&pdev->dev); ret = devm_spi_register_master(&pdev->dev, master); if (ret) { From d73e873bcfff86b0de7dae2610131b50a2970f88 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:13 +0200 Subject: [PATCH 008/127] regulator: fix a kernel-doc markup It seems that the function was renamed. kernel-doc markup should follow it. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/dffad16d4d6427d7d0fc89797e4126fe7c69d5de.1603469755.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a4ffd71696da..79d00e7039d9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5529,7 +5529,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data) EXPORT_SYMBOL_GPL(regulator_set_drvdata); /** - * regulator_get_id - get regulator ID + * rdev_get_id - get regulator ID * @rdev: regulator */ int rdev_get_id(struct regulator_dev *rdev) From ce2424d76f23be93ff745e3101630ce63224bb86 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:16 +0200 Subject: [PATCH 009/127] spi: fix a typo inside a kernel-doc markup spi_split_tranfers_maxsize -> spi_split_transfers_maxsize Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/a103f4f48735caa1a09fad94c5d76e73e2ce37b8.1603469755.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 0cab239d8e7f..22679c8645db 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3193,9 +3193,9 @@ static int __spi_split_transfer_maxsize(struct spi_controller *ctlr, } /** - * spi_split_tranfers_maxsize - split spi transfers into multiple transfers - * when an individual transfer exceeds a - * certain size + * spi_split_transfers_maxsize - split spi transfers into multiple transfers + * when an individual transfer exceeds a + * certain size * @ctlr: the @spi_controller for this transfer * @msg: the @spi_message to transform * @maxsize: the maximum when to apply this From 23fc86eb2f30fc975e5705bb1a2cf92956d2edd7 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 30 Oct 2020 14:11:16 +0200 Subject: [PATCH 010/127] spi: atmel: Downgrade to dev_dbg when dma_request_chan() fails The IP's DMA capabilities are described in the SoC dtsi, to spare users duplicating the DMA bindings in their board device tree. Users that don't want to use DMA, have to overwrite the DMA bindings in their board device tree. An example is: commit ddcdaeb88242 ("ARM: dts: at91: sama5d2: Add DMA bindings for the SPI and UART flx4 functions") When the DMA bindings are overwritten, one could see on the console: atmel_spi fc018400.spi: error -ENODEV: No TX DMA channel, DMA is disabled atmel_spi fc018400.spi: Atmel SPI Controller using PIO only Choosing to not use DMA is not a reason to print an error message. More, the user is already informed when PIO is used: "Atmel SPI Controller using PIO only". Downgrade to dev_dbg when dma_request_chan() fails. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201030121116.869105-1-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 0e5e64a80848..948396b382d7 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -512,8 +512,8 @@ static int atmel_spi_configure_dma(struct spi_master *master, master->dma_tx = dma_request_chan(dev, "tx"); if (IS_ERR(master->dma_tx)) { - err = dev_err_probe(dev, PTR_ERR(master->dma_tx), - "No TX DMA channel, DMA is disabled\n"); + err = PTR_ERR(master->dma_tx); + dev_dbg(dev, "No TX DMA channel, DMA is disabled\n"); goto error_clear; } @@ -524,7 +524,7 @@ static int atmel_spi_configure_dma(struct spi_master *master, * No reason to check EPROBE_DEFER here since we have already * requested tx channel. */ - dev_err(dev, "No RX DMA channel, DMA is disabled\n"); + dev_dbg(dev, "No RX DMA channel, DMA is disabled\n"); goto error; } From ee5558a9084584015c8754ffd029ce14a5827fa8 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Mon, 2 Nov 2020 22:56:51 +0800 Subject: [PATCH 011/127] spi: img-spfi: fix reference leak in img_spfi_resume pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in img_spfi_resume, so we should fix it. Fixes: deba25800a12b ("spi: Add driver for IMG SPFI controller") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201102145651.3875-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index b068537375d6..5f05d519fbbd 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -731,8 +731,10 @@ static int img_spfi_resume(struct device *dev) int ret; ret = pm_runtime_get_sync(dev); - if (ret) + if (ret) { + pm_runtime_put_noidle(dev); return ret; + } spfi_reset(spfi); pm_runtime_put(dev); From a9c52d42814a44472fe1205775320ec20f556908 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 31 Oct 2020 11:30:42 +0800 Subject: [PATCH 012/127] spi: spi-mem: Fix passing zero to 'PTR_ERR' warning Fix smatch warning: drivers/spi/spi-mem.c:746 spi_mem_probe() warn: passing zero to 'PTR_ERR' Fixes: 5d27a9c8ea9e ("spi: spi-mem: Extend the SPI mem interface to set a custom memory name") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20201031033042.42892-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index ef53290b7d24..a1b4d085834a 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -743,7 +743,7 @@ static int spi_mem_probe(struct spi_device *spi) mem->name = dev_name(&spi->dev); if (IS_ERR_OR_NULL(mem->name)) - return PTR_ERR(mem->name); + return PTR_ERR_OR_ZERO(mem->name); spi_set_drvdata(spi, mem); From e6ff10f24c587c1af705b898761e5df615fb0e1a Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 3 Nov 2020 11:00:21 +0100 Subject: [PATCH 013/127] regulator: Add support for DA9121 regulator Add support for the Dialog Semiconductor DA9121, a single-channel dual-phase buck converter controlled via I2C. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20201103100021.19603-3-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 12 +++ drivers/regulator/Makefile | 1 + drivers/regulator/da9121-regulator.c | 108 +++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/regulator/da9121-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 020a00d6696b..005a6036dd38 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -303,6 +303,18 @@ config REGULATOR_DA9063 This driver can also be built as a module. If so, the module will be called da9063-regulator. +config REGULATOR_DA9121 + tristate "Dialog Semiconductor DA9121 regulator" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9121. The + DA9210 is a dual-phase buck converter controlled through an I2C + interface. + + This driver can also be built as a module. If so, the module + will be called da9121-regulator. + config REGULATOR_DA9210 tristate "Dialog Semiconductor DA9210 regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6ebae516258e..6096862a1d60 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o +obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c new file mode 100644 index 000000000000..66bdfd1979c0 --- /dev/null +++ b/drivers/regulator/da9121-regulator.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2020 Axis Communications AB */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DA9121_BUCK_BUCK1_0 0x20 +#define DA9121_BUCK_BUCK1_0_CH1_EN BIT(0) + +#define DA9121_BUCK_BUCK1_5 0x25 +#define DA9121_BUCK_BUCK1_5_CH1_A_VOUT GENMASK(7, 0) + +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + +static const struct regmap_config da9121_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regulator_ops da9121_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static const struct regulator_desc da9121_reg = { + .name = "da9121", + .of_match = "buck1", + .owner = THIS_MODULE, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .vsel_reg = DA9121_BUCK_BUCK1_5, + .vsel_mask = DA9121_BUCK_BUCK1_5_CH1_A_VOUT, + .enable_reg = DA9121_BUCK_BUCK1_0, + .enable_mask = DA9121_BUCK_BUCK1_0_CH1_EN, + /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ + .ramp_delay = 20000, + /* tBUCK_EN */ + .enable_time = 20, +}; + +static const struct of_device_id da9121_dt_ids[] = { + { .compatible = "dlg,da9121", }, + { } +}; +MODULE_DEVICE_TABLE(of, da9121_dt_ids); + +static int da9121_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct regulator_config config = {}; + struct regulator_dev *rdev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + config.dev = &i2c->dev; + config.of_node = dev->of_node; + config.regmap = regmap; + + rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register da9121 regulator\n"); + return PTR_ERR(rdev); + } + + return 0; +} + +static const struct i2c_device_id da9121_i2c_id[] = { + { "da9121", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); + +static struct i2c_driver da9121_regulator_driver = { + .driver = { + .name = "da9121", + .of_match_table = of_match_ptr(da9121_dt_ids), + }, + .probe = da9121_i2c_probe, + .id_table = da9121_i2c_id, +}; + +module_i2c_driver(da9121_regulator_driver); + +MODULE_LICENSE("GPL v2"); From 1119c59404141200125af31f775d3fbbba52c651 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 3 Nov 2020 11:00:20 +0100 Subject: [PATCH 014/127] regulator: Add DA9121 Add bindings for the Dialog Semiconductor DA9121 voltage regulator. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20201103100021.19603-2-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- .../bindings/regulator/dlg,da9121.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/dlg,da9121.yaml diff --git a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml new file mode 100644 index 000000000000..cde0d82dd201 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/dlg,da9121.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dialog Semiconductor DA9121 voltage regulator + +maintainers: + - Vincent Whitchurch + +properties: + compatible: + const: dlg,da9121 + + reg: + maxItems: 1 + + buck1: + description: + Initial data for the Buck1 regulator. + $ref: "regulator.yaml#" + type: object + +unevaluatedProperties: false + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + regulator@68 { + compatible = "dlg,da9121"; + reg = <0x68>; + + buck1 { + regulator-min-microvolt = <680000>; + regulator-max-microvolt = <820000>; + }; + }; + }; + +... From c02bb16b0e826bf0e19aa42c3ae60ea339f32cf5 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 3 Nov 2020 22:09:10 +0800 Subject: [PATCH 015/127] spi: spi-mem: fix reference leak in spi_mem_access_start pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in spi_mem_access_start, so we should fix it. Fixes: f86c24f479530 ("spi: spi-mem: Split spi_mem_exec_op() code") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201103140910.3482-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index a1b4d085834a..f3a3f196e628 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -243,6 +243,7 @@ static int spi_mem_access_start(struct spi_mem *mem) ret = pm_runtime_get_sync(ctlr->dev.parent); if (ret < 0) { + pm_runtime_put_noidle(ctlr->dev.parent); dev_err(&ctlr->dev, "Failed to power device: %d\n", ret); return ret; From 900ccdcb79bb61471df1566a70b2b39687a628d5 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Fri, 6 Nov 2020 09:52:17 +0800 Subject: [PATCH 016/127] spi: stm32: fix reference leak in stm32_spi_resume pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in stm32_spi_resume, so we should fix it. Fixes: db96bf976a4fc ("spi: stm32: fixes suspend/resume management") Signed-off-by: Zhang Qilong Reviewed-by: Alain Volmat Link: https://lore.kernel.org/r/20201106015217.140476-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 2cc850eb8922..471dedf3d339 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2062,6 +2062,7 @@ static int stm32_spi_resume(struct device *dev) ret = pm_runtime_get_sync(dev); if (ret < 0) { + pm_runtime_put_noidle(dev); dev_err(dev, "Unable to power device:%d\n", ret); return ret; } From 285654130d5c1bed000be6b94cd43b5110d16090 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Mon, 9 Nov 2020 09:54:37 +0100 Subject: [PATCH 017/127] regulator: da9121: Use additionalProperties There's no $ref at the top level so use additionalProperties instead of unevaluatedProperties. This fixes the following warning with the latest dtschema: Documentation/devicetree/bindings/regulator/dlg,da9121.yaml: 'additionalProperties' is a required property Reported-by: Rob Herring Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20201109085438.16230-1-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/dlg,da9121.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml index cde0d82dd201..2ece46ee1a59 100644 --- a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml +++ b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml @@ -22,7 +22,7 @@ properties: $ref: "regulator.yaml#" type: object -unevaluatedProperties: false +additionalProperties: false required: - compatible From 88e1419b5ee30cc50e0c4d5265bdee1ba04af539 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Fri, 6 Nov 2020 09:53:57 +0800 Subject: [PATCH 018/127] spi: stm32-qspi: fix reference leak in stm32 qspi operations pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in two callers(stm32_qspi_exec_op and stm32_qspi_setup), so we should fix it. Fixes: 9d282c17b023a ("spi: stm32-qspi: Add pm_runtime support") Signed-off-by: Zhang Qilong Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20201106015357.141235-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index a900962b4336..947e6b9dc9f4 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -434,8 +434,10 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) int ret; ret = pm_runtime_get_sync(qspi->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(qspi->dev); return ret; + } mutex_lock(&qspi->lock); ret = stm32_qspi_send(mem, op); @@ -462,8 +464,10 @@ static int stm32_qspi_setup(struct spi_device *spi) return -EINVAL; ret = pm_runtime_get_sync(qspi->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(qspi->dev); return ret; + } presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1; From 45c0cba753641e5d7c3207f04241bd0e7a021698 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 3 Nov 2020 22:09:47 +0800 Subject: [PATCH 019/127] spi: spi-ti-qspi: fix reference leak in ti_qspi_setup pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in ti_qspi_setup, so we should fix it. Fixes: 505a14954e2d7 ("spi/qspi: Add qspi flash controller") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201103140947.3815-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 3c41649698a5..9417385c0921 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -174,6 +174,7 @@ static int ti_qspi_setup(struct spi_device *spi) ret = pm_runtime_get_sync(qspi->dev); if (ret < 0) { + pm_runtime_put_noidle(qspi->dev); dev_err(qspi->dev, "pm_runtime_get_sync() failed\n"); return ret; } From 702b15cb97123cedcec56a39d9a21c5288eb9ae1 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 3 Nov 2020 15:49:12 +0800 Subject: [PATCH 020/127] spi: mt7621: fix missing clk_disable_unprepare() on error in mt7621_spi_probe Fix the missing clk_disable_unprepare() before return from mt7621_spi_probe in the error handling case. Fixes: cbd66c626e16 ("spi: mt7621: Move SPI driver out of staging") Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20201103074912.195576-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index 2c3b7a2a1ec7..2cdae7994e2a 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -353,6 +353,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) master = spi_alloc_master(&pdev->dev, sizeof(*rs)); if (!master) { dev_info(&pdev->dev, "master allocation failed\n"); + clk_disable_unprepare(clk); return -ENOMEM; } @@ -377,6 +378,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) ret = device_reset(&pdev->dev); if (ret) { dev_err(&pdev->dev, "SPI reset failed!\n"); + clk_disable_unprepare(clk); return ret; } From 763eab7074f6e71babd85d796156f05a675f9510 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 3 Nov 2020 22:13:45 +0800 Subject: [PATCH 021/127] spi: tegra20-slink: fix reference leak in slink ops of tegra20 pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in two callers(tegra_slink_setup and tegra_slink_resume), so we should fix it. Fixes: dc4dc36056392 ("spi: tegra: add spi driver for SLINK controller") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201103141345.6188-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index a0810765d4e5..f7c832fd4003 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -751,6 +751,7 @@ static int tegra_slink_setup(struct spi_device *spi) ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { + pm_runtime_put_noidle(tspi->dev); dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); return ret; } @@ -1188,6 +1189,7 @@ static int tegra_slink_resume(struct device *dev) ret = pm_runtime_get_sync(dev); if (ret < 0) { + pm_runtime_put_noidle(dev); dev_err(dev, "pm runtime failed, e = %d\n", ret); return ret; } From 3482e797ab688da6703fe18d8bad52f94199f4f2 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 3 Nov 2020 22:13:23 +0800 Subject: [PATCH 022/127] spi: tegra20-sflash: fix reference leak in tegra_sflash_resume pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in tegra_sflash_resume, so we should fix it. Fixes: 8528547bcc336 ("spi: tegra: add spi driver for sflash controller") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201103141323.5841-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-sflash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index b59015c7c8a8..cfb7de737937 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -552,6 +552,7 @@ static int tegra_sflash_resume(struct device *dev) ret = pm_runtime_get_sync(dev); if (ret < 0) { + pm_runtime_put_noidle(dev); dev_err(dev, "pm runtime failed, e = %d\n", ret); return ret; } From a042184c7fb99961ea083d4ec192614bec671969 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 3 Nov 2020 22:13:06 +0800 Subject: [PATCH 023/127] spi: tegra114: fix reference leak in tegra spi ops pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in two callers(tegra_spi_setup and tegra_spi_resume), so we should fix it. Fixes: f333a331adfac ("spi/tegra114: add spi driver") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201103141306.5607-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index ca6886aaa519..a2e5907276e7 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -966,6 +966,7 @@ static int tegra_spi_setup(struct spi_device *spi) ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { + pm_runtime_put_noidle(tspi->dev); dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); if (cdata) tegra_spi_cleanup(spi); @@ -1474,6 +1475,7 @@ static int tegra_spi_resume(struct device *dev) ret = pm_runtime_get_sync(dev); if (ret < 0) { + pm_runtime_put_noidle(dev); dev_err(dev, "pm runtime failed, e = %d\n", ret); return ret; } From 9bb9ef2b3e5d9d012876e7e2d7757eb30e865bee Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 3 Nov 2020 15:49:11 +0800 Subject: [PATCH 024/127] spi: bcm63xx-hsspi: fix missing clk_disable_unprepare() on error in bcm63xx_hsspi_resume Fix the missing clk_disable_unprepare() before return from bcm63xx_hsspi_resume in the error handling case when fails to prepare and enable bs->pll_clk. Fixes: 0fd85869c2a9 ("spi/bcm63xx-hsspi: keep pll clk enabled") Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20201103074911.195530-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx-hsspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 9909b18f3c5a..1f08d7553f07 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -494,8 +494,10 @@ static int bcm63xx_hsspi_resume(struct device *dev) if (bs->pll_clk) { ret = clk_prepare_enable(bs->pll_clk); - if (ret) + if (ret) { + clk_disable_unprepare(bs->clk); return ret; + } } spi_master_resume(master); From 0e685017c7ba1a2fe9f6f1e7a9302890747d934c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Nov 2020 23:41:00 +0100 Subject: [PATCH 025/127] spi: atmel-quadspi: Disable clock in probe error path If the call to of_device_get_match_data() fails on probe of the Atmel QuadSPI driver, the clock "aq->pclk" is erroneously not unprepared and disabled. Fix it. Fixes: 2e5c88887358 ("spi: atmel-quadspi: add support for sam9x60 qspi controller") Signed-off-by: Lukas Wunner Cc: # v5.1+ Cc: Tudor Ambarus Cc: Boris Brezillon Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/8f8dc2815aa97b2378528f08f923bf81e19611f0.1604874488.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 8c009c175f2c..b44521d4a245 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -594,7 +594,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) if (!aq->caps) { dev_err(&pdev->dev, "Could not retrieve QSPI caps\n"); err = -EINVAL; - goto exit; + goto disable_pclk; } if (aq->caps->has_qspick) { From 1dcbdd944824369d4569959f8130336fe6fe5f39 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Mon, 2 Nov 2020 22:58:35 +0800 Subject: [PATCH 026/127] spi: imx: fix reference leak in two imx operations pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in callers(spi_imx_prepare_message and spi_imx_remove), so we should fix it. Fixes: 525c9e5a32bd7 ("spi: imx: enable runtime pm support") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201102145835.4765-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 4b80e27ecdbf..8911465d8f47 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1538,6 +1538,7 @@ spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg) ret = pm_runtime_get_sync(spi_imx->dev); if (ret < 0) { + pm_runtime_put_noidle(spi_imx->dev); dev_err(spi_imx->dev, "failed to enable clock\n"); return ret; } @@ -1747,6 +1748,7 @@ static int spi_imx_remove(struct platform_device *pdev) ret = pm_runtime_get_sync(spi_imx->dev); if (ret < 0) { + pm_runtime_put_noidle(spi_imx->dev); dev_err(spi_imx->dev, "failed to enable clock\n"); return ret; } From 1e908b2419ea828dfad9819e5c01322a93665356 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Nov 2020 10:19:38 +0200 Subject: [PATCH 027/127] regulator: BD71837 support commonly used feedback connection The BD71837 buck output voltages are in a few cases scaled using external connection which adds a pull-up to regulator feedback pin. This connection will adjust output voltage from regulator in a deterministic way. Add support for describing this HW connection so that driver can adjust voltage ranges accordingly. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/9959924313db7c7165598604f9a07bf227f471a8.1604994184.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown --- .../regulator/rohm,bd71837-regulator.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml index f5e31196a646..1941b36cf1ef 100644 --- a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml @@ -105,6 +105,54 @@ patternProperties: PMIC hardware state machine. type: boolean + # Setups where regulator (especially the buck8) output voltage is scaled + # by adding external connection where some other regulator output is + # connected to feedback-pin (over suitable resistors) is getting popular + # amongst users of BD71837. (This allows for example scaling down the + # buck8 voltages to suit lover GPU voltages for projects where buck8 is + # (ab)used to supply power for GPU. + # + # So we allow describing this external connection from DT and scale the + # voltages accordingly. This is what the connection should look like: + # + # |---------------| + # | buck 8 |-------+----->Vout + # | | | + # |---------------| | + # | | + # | | + # +-------+--R2----+ + # | + # R1 + # | + # V FB-pull-up + # + # Here the buck output is sifted according to formula: + # + # Vout_o = Vo - (Vpu - Vo)*R2/R1 + # Linear_step = step_orig*(R1+R2)/R1 + # + # where: + # Vout_o is adjusted voltage output at vsel reg value 0 + # Vo is original voltage output at vsel reg value 0 + # Vpu is the pull-up voltage V FB-pull-up in the picture + # R1 and R2 are resistor values. + + rohm,fb-pull-up-microvolt: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used pull-up voltage before R1. + + rohm,feedback-pull-up-r1-ohms: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used R1 resistor. + + rohm,feedback-pull-up-r2-ohms: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used R2 resistor. + required: - regulator-name From b54a27d8109fc8f18cec3e0663f8e81ea727e3c6 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Nov 2020 10:19:58 +0200 Subject: [PATCH 028/127] regulator: BD71847 support commonly used feedback connection The BD71847 buck output voltages are in a few cases scaled using external connection which adds a pull-up to regulator feedback pin. This connection will adjust output voltage from regulator in a deterministic way. Add support for describing this HW connection so that driver can adjust voltage ranges accordingly. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/9b6b3d8233071d478f7d1e93b498f5a2141941e6.1604994184.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown --- .../regulator/rohm,bd71847-regulator.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml index eeac32cd15d6..a1b806373853 100644 --- a/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml @@ -99,6 +99,55 @@ patternProperties: Enable/Disable control of this regulator must be left to the PMIC hardware state machine. type: boolean + + # Setups where regulator (especially the buck8) output voltage is scaled + # by adding external connection where some other regulator output is + # connected to feedback-pin (over suitable resistors) is getting popular + # amongst users of BD71837. (This allows for example scaling down the + # buck8 voltages to suit lover GPU voltages for projects where buck8 is + # (ab)used to supply power for GPU. + # + # So we allow describing this external connection from DT and scale the + # voltages accordingly. This is what the connection should look like: + # + # |---------------| + # | buck 8 |-------+----->Vout + # | | | + # |---------------| | + # | | + # | | + # +-------+--R2----+ + # | + # R1 + # | + # V FB-pull-up + # + # Here the buck output is sifted according to formula: + # + # Vout_o = Vo - (Vpu - Vo)*R2/R1 + # Linear_step = step_orig*(R1+R2)/R1 + # + # where: + # Vout_o is adjusted voltage output at vsel reg value 0 + # Vo is original voltage output at vsel reg value 0 + # Vpu is the pull-up voltage V FB-pull-up in the picture + # R1 and R2 are resistor values. + + rohm,fb-pull-up-microvolt: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used pull-up voltage before R1. + + rohm,feedback-pull-up-r1-ohms: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used R1 resistor. + + rohm,feedback-pull-up-r2-ohms: + description: + Feedback-pin has pull-up connection to adjust voltage range. This is + the used R2 resistor. + required: - regulator-name From d2ad981151b3a812e961c8ee0ffd7e349b4027d6 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Nov 2020 10:20:17 +0200 Subject: [PATCH 029/127] regulator: bd718x7: Support external connection to scale voltages Setups where regulator (especially the buck8) output voltage is scaled by adding external connection where some other regulator output is connected to feedback-pin (over suitable resistors) is getting popular amongst users of BD71837. This allows for example scaling down the buck8 voltages to suit lover GPU voltages for projects where buck8 is (ab)used to supply power for GPU. As a note - some setups do allow DVS for buck8. This do produce voltage spikes and the HW must be evaluated to be able to survive them. Thus this commit still keep the DVS disabled for non DVS bucks by default. Let's not help you burn your proto board. Allow describing this external connection from DT and scale the voltages accordingly. This is what the connection should look like: |------------| | buck 8 |-------+----->Vout | | | |------------| | | FB pin | | | +-------+--R2---+ | R1 | V FB-pull-up Here the buck output is sifted according to formula: Vout_o = Vo - (Vpu - Vo)*R2/R1 Linear_step = step_orig*(R1+R2)/R1 where: Vout_o is adjusted voltage output at vsel reg value 0 Vo is original voltage output at vsel reg value 0 Vpu is the pull-up voltage V FB-pull-up in the picture R1 and R2 are resistor values. Bring support for specifying the Vpu, R1 and R2 from device tree and scale voltages if they are given. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/89b2be87074f307a8823f15f34e1f662023cbf36.1604994184.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 164 ++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 0774467994fb..e6d5d98c3cea 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np, dev_warn(dev, "Bad regulator node\n"); } -static int get_hw_controlled_regulators(struct device *dev, - struct bd718xx_regulator_data *reg_data, - unsigned int num_reg_data, int *info) +/* + * Setups where regulator (especially the buck8) output voltage is scaled + * by adding external connection where some other regulator output is connected + * to feedback-pin (over suitable resistors) is getting popular amongst users + * of BD71837. (This allows for example scaling down the buck8 voltages to suit + * lover GPU voltages for projects where buck8 is (ab)used to supply power + * for GPU. Additionally some setups do allow DVS for buck8 but as this do + * produce voltage spikes the HW must be evaluated to be able to survive this + * - hence I keep the DVS disabled for non DVS bucks by default. I don't want + * to help you burn your proto board) + * + * So we allow describing this external connection from DT and scale the + * voltages accordingly. This is what the connection should look like: + * + * |------------| + * | buck 8 |-------+----->Vout + * | | | + * |------------| | + * | FB pin | + * | | + * +-------+--R2---+ + * | + * R1 + * | + * V FB-pull-up + * + * Here the buck output is sifted according to formula: + * + * Vout_o = Vo - (Vpu - Vo)*R2/R1 + * Linear_step = step_orig*(R1+R2)/R1 + * + * where: + * Vout_o is adjusted voltage output at vsel reg value 0 + * Vo is original voltage output at vsel reg value 0 + * Vpu is the pull-up voltage V FB-pull-up in the picture + * R1 and R2 are resistor values. + * + * As a real world example for buck8 and a specific GPU: + * VLDO = 1.6V (used as FB-pull-up) + * R1 = 1000ohms + * R2 = 150ohms + * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V + * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV + */ +static int setup_feedback_loop(struct device *dev, struct device_node *np, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int fb_uv) { + int i, r1, r2, ret; + + /* + * We do adjust the values in the global desc based on DT settings. + * This may not be best approach as it can cause problems if more than + * one PMIC is controlled from same processor. I don't see such use-case + * for BD718x7 now - so we spare some bits. + * + * If this will point out to be a problem - then we can allocate new + * bd718xx_regulator_data array at probe and just use the global + * array as a template where we copy initial values. Then we can + * use allocated descs for regultor registration and do IC specific + * modifications to this copy while leaving other PMICs untouched. But + * that means allocating new array for each PMIC - and currently I see + * no need for that. + */ + + for (i = 0; i < num_reg_data; i++) { + struct regulator_desc *desc = ®_data[i].desc; + int j; + + if (!of_node_name_eq(np, desc->of_match)) + continue; + + pr_info("Looking at node '%s'\n", desc->of_match); + + /* The feedback loop connection does not make sense for LDOs */ + if (desc->id >= BD718XX_LDO1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms", + &r1); + if (ret) + return ret; + + if (!r1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms", + &r2); + if (ret) + return ret; + + if (desc->n_linear_ranges && desc->linear_ranges) { + struct linear_range *new; + + new = devm_kzalloc(dev, desc->n_linear_ranges * + sizeof(struct linear_range), + GFP_KERNEL); + if (!new) + return -ENOMEM; + + for (j = 0; j < desc->n_linear_ranges; j++) { + int min = desc->linear_ranges[j].min; + int step = desc->linear_ranges[j].step; + + min -= (fb_uv - min)*r2/r1; + step = step * (r1 + r2); + step /= r1; + + new[j].min = min; + new[j].step = step; + + dev_dbg(dev, "%s: old range min %d, step %d\n", + desc->name, desc->linear_ranges[j].min, + desc->linear_ranges[j].step); + dev_dbg(dev, "new range min %d, step %d\n", min, + step); + } + desc->linear_ranges = new; + } + dev_dbg(dev, "regulator '%s' has FB pull-up configured\n", + desc->name); + + return 0; + } + + return -ENODEV; +} + +static int get_special_regulators(struct device *dev, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int *info) +{ + int ret; struct device_node *np; struct device_node *nproot = dev->of_node; - const char *prop = "rohm,no-regulator-enable-control"; + int uv; *info = 0; @@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev, dev_err(dev, "failed to find regulators node\n"); return -ENODEV; } - for_each_child_of_node(nproot, np) - if (of_property_read_bool(np, prop)) + for_each_child_of_node(nproot, np) { + if (of_property_read_bool(np, "rohm,no-regulator-enable-control")) mark_hw_controlled(dev, np, reg_data, num_reg_data, info); + ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt", + &uv); + if (ret) { + if (ret == -EINVAL) + continue; + else + goto err_out; + } + + ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv); + if (ret) + goto err_out; + } of_node_put(nproot); return 0; + +err_out: + of_node_put(np); + of_node_put(nproot); + + return ret; } static int bd718xx_probe(struct platform_device *pdev) @@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev) * be affected by PMIC state machine - Eg. regulator is likely to stay * on even in SUSPEND */ - get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data, + err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data, &omit_enable); + if (err) + return err; for (i = 0; i < num_reg_data; i++) { From 029b42d8519cef70c4fb5fcaccd08f1053ed2bf0 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 27 Oct 2020 10:57:23 +0100 Subject: [PATCH 030/127] spi: introduce SPI_MODE_X_MASK macro Provide a macro to filter all SPI_MODE_0,1,2,3 mode in one run. The latest SPI framework will parse the devicetree in following call sequence: of_register_spi_device() -> of_spi_parse_dt() So, driver do not need to pars the devicetree and will get prepared flags in the probe. On one hand it is good far most drivers. On other hand some drivers need to filter flags provide by SPI framework and apply know to work flags. This drivers may use SPI_MODE_X_MASK to filter MODE flags and set own, known flags: spi->flags &= ~SPI_MODE_X_MASK; spi->flags |= SPI_MODE_0; Signed-off-by: Oleksij Rempel Link: https://lore.kernel.org/r/20201027095724.18654-2-o.rempel@pengutronix.de Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 99380c0825db..8097f27702f3 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -171,6 +171,7 @@ struct spi_device { #define SPI_MODE_1 (0|SPI_CPHA) #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) +#define SPI_MODE_X_MASK (SPI_CPOL|SPI_CPHA) #define SPI_CS_HIGH 0x04 /* chipselect active high? */ #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ #define SPI_3WIRE 0x10 /* SI/SO signals shared */ From e4062765bc2a41e025e29dd56bad798505036427 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Fri, 6 Nov 2020 09:50:35 +0800 Subject: [PATCH 031/127] spi: sprd: fix reference leak in sprd_spi_remove pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in sprd_spi_remove, so we should fix it. Fixes: e7d973a31c24b ("spi: sprd: Add SPI driver for Spreadtrum SC9860") Signed-off-by: Zhang Qilong Acked-by: Chunyan Zhang Link: https://lore.kernel.org/r/20201106015035.139574-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 635738f54c73..b41a75749b49 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -1010,6 +1010,7 @@ static int sprd_spi_remove(struct platform_device *pdev) ret = pm_runtime_get_sync(ss->dev); if (ret < 0) { + pm_runtime_put_noidle(ss->dev); dev_err(ss->dev, "failed to resume SPI controller\n"); return ret; } From 8853b2503014aca5c793d2c7f0aabc990b32bdad Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Nov 2020 23:41:00 +0100 Subject: [PATCH 032/127] spi: synquacer: Disable clock in probe error path If the calls to platform_get_irq() or devm_request_irq() fail on probe of the SynQuacer SPI driver, the clock "sspi->clk" is erroneously not unprepared and disabled. If the clock rate "master->max_speed_hz" cannot be determined, the same happens and in addition the spi_master struct is not freed. Fix it. Fixes: b0823ee35cf9 ("spi: Add spi driver for Socionext SynQuacer platform") Signed-off-by: Lukas Wunner Cc: # v5.3+ Cc: Masahisa Kojima Link: https://lore.kernel.org/r/232281df1ab91d8f0f553a62d5f97fc264ace4da.1604874488.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-synquacer.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index 42e82dbe3d41..8cdca6ab8098 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -657,7 +657,8 @@ static int synquacer_spi_probe(struct platform_device *pdev) if (!master->max_speed_hz) { dev_err(&pdev->dev, "missing clock source\n"); - return -EINVAL; + ret = -EINVAL; + goto disable_clk; } master->min_speed_hz = master->max_speed_hz / 254; @@ -670,7 +671,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) rx_irq = platform_get_irq(pdev, 0); if (rx_irq <= 0) { ret = rx_irq; - goto put_spi; + goto disable_clk; } snprintf(sspi->rx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-rx", dev_name(&pdev->dev)); @@ -678,13 +679,13 @@ static int synquacer_spi_probe(struct platform_device *pdev) 0, sspi->rx_irq_name, sspi); if (ret) { dev_err(&pdev->dev, "request rx_irq failed (%d)\n", ret); - goto put_spi; + goto disable_clk; } tx_irq = platform_get_irq(pdev, 1); if (tx_irq <= 0) { ret = tx_irq; - goto put_spi; + goto disable_clk; } snprintf(sspi->tx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-tx", dev_name(&pdev->dev)); @@ -692,7 +693,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) 0, sspi->tx_irq_name, sspi); if (ret) { dev_err(&pdev->dev, "request tx_irq failed (%d)\n", ret); - goto put_spi; + goto disable_clk; } master->dev.of_node = np; @@ -710,7 +711,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) ret = synquacer_spi_enable(master); if (ret) - goto fail_enable; + goto disable_clk; pm_runtime_set_active(sspi->dev); pm_runtime_enable(sspi->dev); @@ -723,7 +724,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) disable_pm: pm_runtime_disable(sspi->dev); -fail_enable: +disable_clk: clk_disable_unprepare(sspi->clk); put_spi: spi_master_put(master); From c575e9113bff5e024d75481613faed5ef9d465b2 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Nov 2020 23:41:00 +0100 Subject: [PATCH 033/127] spi: pic32: Don't leak DMA channels in probe error path If the calls to devm_request_irq() or devm_spi_register_master() fail on probe of the PIC32 SPI driver, the DMA channels requested by pic32_spi_dma_prep() are erroneously not released. Plug the leak. Fixes: 1bcb9f8ceb67 ("spi: spi-pic32: Add PIC32 SPI master driver") Signed-off-by: Lukas Wunner Cc: # v4.7+ Cc: Purna Chandra Mandal Link: https://lore.kernel.org/r/9624250e3a7aa61274b38219a62375bac1def637.1604874488.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-pic32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 156961b4ca86..104bde153efd 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -839,6 +839,7 @@ static int pic32_spi_probe(struct platform_device *pdev) return 0; err_bailout: + pic32_spi_dma_unprep(pic32s); clk_disable_unprepare(pic32s->clk); err_master: spi_master_put(master); From 5ef76dac0f2c26aeae4ee79eb830280f16d5aceb Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Nov 2020 23:41:00 +0100 Subject: [PATCH 034/127] spi: st-ssc4: Fix unbalanced pm_runtime_disable() in probe error path If the calls to devm_platform_ioremap_resource(), irq_of_parse_and_map() or devm_request_irq() fail on probe of the ST SSC4 SPI driver, the runtime PM disable depth is incremented even though it was not decremented before. Fix it. Fixes: cd050abeba2a ("spi: st-ssc4: add missed pm_runtime_disable") Signed-off-by: Lukas Wunner Cc: # v5.5+ Cc: Chuhong Yuan Link: https://lore.kernel.org/r/fbe8768c30dc829e2d77eabe7be062ca22f84024.1604874488.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 77d26d64541a..6c44dda9ee8c 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -375,13 +375,14 @@ static int spi_st_probe(struct platform_device *pdev) ret = devm_spi_register_master(&pdev->dev, master); if (ret) { dev_err(&pdev->dev, "Failed to register master\n"); - goto clk_disable; + goto rpm_disable; } return 0; -clk_disable: +rpm_disable: pm_runtime_disable(&pdev->dev); +clk_disable: clk_disable_unprepare(spi_st->clk); put_master: spi_master_put(master); From f7d01359b0d96331fce7bd4051d3b68a4bb5ea83 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 12 Nov 2020 15:02:17 +0000 Subject: [PATCH 035/127] regmap: Fix order of regmap write log _regmap_write can trigger a _regmap_select_page, which will call another _regmap_write that will be executed first, but the log shows the inverse order Also, keep consistency with _regmap_read which only logs in case of success Signed-off-by: Lucas Tanure Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20201112150217.459844-1-tanureal@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 5db536ccfcd6..297e95be25b3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1924,12 +1924,15 @@ int _regmap_write(struct regmap *map, unsigned int reg, } } - if (regmap_should_log(map)) - dev_info(map->dev, "%x <= %x\n", reg, val); + ret = map->reg_write(context, reg, val); + if (ret == 0) { + if (regmap_should_log(map)) + dev_info(map->dev, "%x <= %x\n", reg, val); - trace_regmap_reg_write(map, reg, val); + trace_regmap_reg_write(map, reg, val); + } - return map->reg_write(context, reg, val); + return ret; } /** From 376ccca853fdb9959f7ac5185a428a9f91e71e86 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 27 Oct 2020 10:57:24 +0100 Subject: [PATCH 036/127] Input: ads7846: do not overwrite spi->mode flags set by spi framework Do not overwrite spi->mode flags set by spi framework, otherwise the chip select polarity will get lost. Signed-off-by: Oleksij Rempel Acked-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20201027095724.18654-3-o.rempel@pengutronix.de Signed-off-by: Mark Brown --- drivers/input/touchscreen/ads7846.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 8fd7fc39c4fd..f2dc2c8ab5ec 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1288,7 +1288,8 @@ static int ads7846_probe(struct spi_device *spi) * may not. So we stick to very-portable 8 bit words, both RX and TX. */ spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; + spi->mode &= ~SPI_MODE_X_MASK; + spi->mode |= SPI_MODE_0; err = spi_setup(spi); if (err < 0) return err; From 0917c9db23accb8690d8f1987ada36eb5b1a5ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Fri, 13 Nov 2020 01:20:27 +0100 Subject: [PATCH 037/127] regulator: debug early supply resolving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Help debugging the case when set_machine_constraints() needs to be repeated. Signed-off-by: MichaÅ‚ MirosÅ‚aw Tested-by: Ahmad Fatoum # stpmic1 Link: https://lore.kernel.org/r/f9cba575580369e46661a9278ee6c6a8d8564c2a.1605226675.git.mirq-linux@rere.qmqm.pl Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2e1ea18221ef..2aa7def7cec6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5299,6 +5299,8 @@ regulator_register(const struct regulator_desc *regulator_desc, /* FIXME: this currently triggers a chicken-and-egg problem * when creating -SUPPLY symlink in sysfs to a regulator * that is just being created */ + rdev_dbg(rdev, "will resolve supply early: %s\n", + rdev->supply_name); ret = regulator_resolve_supply(rdev); if (!ret) ret = set_machine_constraints(rdev, constraints); From 478f8089161e9a8f487ef3f560e59d1423b81c05 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:07 +0200 Subject: [PATCH 038/127] regulator: mcp16502: add linear_min_sel Selectors b/w zero and VDD_LOW_SEL are not valid. Use linear_min_sel. Fixes: 919261c03e7ca ("regulator: mcp16502: add regulator driver for MCP16502") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-4-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 6d0ad74935b3..ab78f831f5bf 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -93,6 +93,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode) .owner = THIS_MODULE, \ .n_voltages = MCP16502_VSEL + 1, \ .linear_ranges = _ranges, \ + .linear_min_sel = VDD_LOW_SEL, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .of_match = of_match_ptr(_name), \ .of_map_mode = mcp16502_of_map_mode, \ From 3e5532a011b09861abc2da3aa518b9aafc250570 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:08 +0200 Subject: [PATCH 039/127] regulator: mcp16502: adapt for get/set on other registers MCP16502 have multiple registers for each regulator (as described in enum mcp16502_reg). Adapt the code to be able to get/set all these registers. This is necessary for the following commits. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-5-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 43 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index ab78f831f5bf..48eb64bc4018 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -54,13 +54,9 @@ * This function is useful for iterating over all regulators and accessing their * registers in a generic way or accessing a regulator device by its id. */ -#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r) #define MCP16502_STAT_BASE(i) ((i) + 5) -#define MCP16502_OFFSET_MODE_A 0 -#define MCP16502_OFFSET_MODE_LPM 1 -#define MCP16502_OFFSET_MODE_HIB 2 - #define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL #define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE #define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY @@ -75,6 +71,23 @@ #define MCP16502_MIN_REG 0x0 #define MCP16502_MAX_REG 0x65 +/** + * enum mcp16502_reg - MCP16502 regulators's registers + * @MCP16502_REG_A: active state register + * @MCP16502_REG_LPM: low power mode state register + * @MCP16502_REG_HIB: hibernate state register + * @MCP16502_REG_SEQ: startup sequence register + * @MCP16502_REG_CFG: configuration register + */ +enum mcp16502_reg { + MCP16502_REG_A, + MCP16502_REG_LPM, + MCP16502_REG_HIB, + MCP16502_REG_HPM, + MCP16502_REG_SEQ, + MCP16502_REG_CFG, +}; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -144,22 +157,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) } /* - * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * mcp16502_get_reg() - get the PMIC's state configuration register for opmode * * @rdev: the regulator whose register we are searching * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate */ -static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode) { - int reg = MCP16502_BASE(rdev_get_id(rdev)); - switch (opmode) { case MCP16502_OPMODE_ACTIVE: - return reg + MCP16502_OFFSET_MODE_A; + return MCP16502_REG_BASE(rdev_get_id(rdev), A); case MCP16502_OPMODE_LPM: - return reg + MCP16502_OFFSET_MODE_LPM; + return MCP16502_REG_BASE(rdev_get_id(rdev), LPM); case MCP16502_OPMODE_HIB: - return reg + MCP16502_OFFSET_MODE_HIB; + return MCP16502_REG_BASE(rdev_get_id(rdev), HIB); default: return -EINVAL; } @@ -179,7 +190,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) unsigned int val; int ret, reg; - reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE); if (reg < 0) return reg; @@ -210,7 +221,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, int val; int reg; - reg = mcp16502_get_reg(rdev, op_mode); + reg = mcp16502_get_state_reg(rdev, op_mode); if (reg < 0) return reg; @@ -269,10 +280,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) { switch (pm_suspend_target_state) { case PM_SUSPEND_STANDBY: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM); case PM_SUSPEND_ON: case PM_SUSPEND_MEM: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB); default: dev_err(&rdev->dev, "invalid suspend target: %d\n", pm_suspend_target_state); From 322eb8666d2f50556e89d73b54cf2dad8703c4e0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:09 +0200 Subject: [PATCH 040/127] regulator: mcp16502: add support for ramp delay MCP16502 have configurable ramp delay support (via DVSR bits in regulators' CFG register). Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-6-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 89 +++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 48eb64bc4018..f81afeeddb19 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -22,8 +22,9 @@ #define VDD_LOW_SEL 0x0D #define VDD_HIGH_SEL 0x3F -#define MCP16502_FLT BIT(7) -#define MCP16502_ENS BIT(0) +#define MCP16502_FLT BIT(7) +#define MCP16502_DVSR GENMASK(3, 2) +#define MCP16502_ENS BIT(0) /* * The PMIC has four sets of registers corresponding to four power modes: @@ -88,6 +89,12 @@ enum mcp16502_reg { MCP16502_REG_CFG, }; +/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */ +static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 }; + +/* Ramp delay (uV/us) for buck2, buck3, buck4. */ +static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 }; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -271,6 +278,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_UNDEFINED; } +static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + static const u8 us_ramp[] = { 8, 16, 24, 32 }; + int id = rdev_get_id(rdev); + unsigned int uV_delta, val; + int ret; + + ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val); + if (ret) + return ret; + + val = (val & MCP16502_DVSR) >> 2; + uV_delta = abs(new_sel * rdev->desc->linear_ranges->step - + old_sel * rdev->desc->linear_ranges->step); + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b1l12[val]); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b234[val]); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + const int *ramp; + int id = rdev_get_id(rdev); + unsigned int i, size; + + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ramp = mcp16502_ramp_b1l12; + size = ARRAY_SIZE(mcp16502_ramp_b1l12); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ramp = mcp16502_ramp_b234; + size = ARRAY_SIZE(mcp16502_ramp_b234); + break; + + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + if (ramp[i] == ramp_delay) + break; + } + if (i == size) + return -EINVAL; + + return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG), + MCP16502_DVSR, (i << 2)); +} + #ifdef CONFIG_SUSPEND /* * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC @@ -365,6 +446,8 @@ static const struct regulator_ops mcp16502_buck_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, .set_mode = mcp16502_set_mode, .get_mode = mcp16502_get_mode, @@ -389,6 +472,8 @@ static const struct regulator_ops mcp16502_ldo_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, #ifdef CONFIG_SUSPEND .set_suspend_voltage = mcp16502_set_suspend_voltage, From 842f44806efaddfae5ecff8f143c2607a4fa65d7 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:10 +0200 Subject: [PATCH 041/127] regulator: mcp16502: remove void documentation of struct mcp16502 struct mcp16502 has no members called rdev or rmap. Remove the documentation. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-7-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index f81afeeddb19..74ad92dc664a 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -135,8 +135,6 @@ enum { /* * struct mcp16502 - PMIC representation - * @rdev: the regulators belonging to this chip - * @rmap: regmap to be used for I2C communication * @lpm: LPM GPIO descriptor */ struct mcp16502 { From bdcd1177578cd5556f7494da86d5038db8203a16 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:05 +0200 Subject: [PATCH 042/127] regulator: core: validate selector against linear_min_sel There are regulators who's min selector is not zero. Selectors loops (looping b/w zero and regulator::desc::n_voltages) might throw errors because invalid selectors are used (lower than regulator::desc::linear_min_sel). For this situations validate selectors against regulator::desc::linear_min_sel. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-2-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 9 +++++++-- drivers/regulator/helpers.c | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2e1ea18221ef..1d0d35f14f37 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,7 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, return rdev->desc->fixed_uV; if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (lock) regulator_lock(rdev); @@ -3109,7 +3110,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; @@ -4032,6 +4034,9 @@ int regulator_set_voltage_time(struct regulator *regulator, for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ + if (i < rdev->desc->linear_min_sel) + continue; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e4bb09bbd3fa..974f1a63993d 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -647,7 +647,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, return -EINVAL; } - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; return rdev->desc->volt_table[selector]; From ab97800e088acf34d0014845ed93605dd5c1ea2a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 19:56:04 +0200 Subject: [PATCH 043/127] regulator: core: do not continue if selector match Do not continue if selector has already been located. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605290164-11556-1-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7de3df6bedbf..85bf278c9378 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4037,6 +4037,9 @@ int regulator_set_voltage_time(struct regulator *regulator, if (i < rdev->desc->linear_min_sel) continue; + if (old_sel >= 0 && new_sel >= 0) + break; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; From f32cce8483f18a098ae50b524f926ef0f2bd2e12 Mon Sep 17 00:00:00 2001 From: bayi cheng Date: Wed, 11 Nov 2020 16:55:02 +0800 Subject: [PATCH 044/127] spi: spi-mtk-nor: add axi clock control for MT8192 spi-nor MT8192 spi-nor is an independent sub system, we need extra control axi bus clock for it. Add support for the additional axi clock to allow it to be configured appropriately. Signed-off-by: bayi cheng Tested-by: Ikjoon Jang Link: https://lore.kernel.org/r/1605084902-13151-2-git-send-email-bayi.cheng@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index b97f26a60cbe..bf2d0f9cdd19 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -103,6 +103,7 @@ struct mtk_nor { dma_addr_t buffer_dma; struct clk *spi_clk; struct clk *ctlr_clk; + struct clk *axi_clk; unsigned int spi_freq; bool wbuf_en; bool has_irq; @@ -672,6 +673,7 @@ static void mtk_nor_disable_clk(struct mtk_nor *sp) { clk_disable_unprepare(sp->spi_clk); clk_disable_unprepare(sp->ctlr_clk); + clk_disable_unprepare(sp->axi_clk); } static int mtk_nor_enable_clk(struct mtk_nor *sp) @@ -688,6 +690,13 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp) return ret; } + ret = clk_prepare_enable(sp->axi_clk); + if (ret) { + clk_disable_unprepare(sp->spi_clk); + clk_disable_unprepare(sp->ctlr_clk); + return ret; + } + return 0; } @@ -746,7 +755,7 @@ static int mtk_nor_probe(struct platform_device *pdev) struct spi_controller *ctlr; struct mtk_nor *sp; void __iomem *base; - struct clk *spi_clk, *ctlr_clk; + struct clk *spi_clk, *ctlr_clk, *axi_clk; int ret, irq; unsigned long dma_bits; @@ -762,6 +771,10 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(ctlr_clk)) return PTR_ERR(ctlr_clk); + axi_clk = devm_clk_get_optional(&pdev->dev, "axi"); + if (IS_ERR(axi_clk)) + return PTR_ERR(axi_clk); + dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev); if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) { dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits); @@ -794,6 +807,7 @@ static int mtk_nor_probe(struct platform_device *pdev) sp->dev = &pdev->dev; sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; + sp->axi_clk = axi_clk; sp->high_dma = (dma_bits > 32); sp->buffer = dmam_alloc_coherent(&pdev->dev, MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, From 1241f0787578136ab58f49adc52f2dcd2bbc4bf2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 15 Nov 2020 20:07:21 -0800 Subject: [PATCH 045/127] spi: dw: fix build error by selecting MULTIPLEXER Fix build error for spi-dw-bt1.o by selecting MULTIPLEXER. hppa-linux-ld: drivers/spi/spi-dw-bt1.o: in function `dw_spi_bt1_sys_init': (.text+0x1ac): undefined reference to `devm_mux_control_get' Fixes: abf00907538e ("spi: dw: Add Baikal-T1 SPI Controller glue driver") Reported-by: kernel test robot Signed-off-by: Randy Dunlap Cc: Serge Semin Cc: Ramil Zaripov Cc: Mark Brown Cc: linux-spi@vger.kernel.org Acked-by: Serge Semin Link: https://lore.kernel.org/r/20201116040721.8001-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5cff60de8e83..3fd16b7f6150 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -255,6 +255,7 @@ config SPI_DW_MMIO config SPI_DW_BT1 tristate "Baikal-T1 SPI driver for DW SPI core" depends on MIPS_BAIKAL_T1 || COMPILE_TEST + select MULTIPLEXER help Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI controllers. Two of them are pretty much normal: with IRQ, DMA, From 6e3dbfcb3c8a1ef8cc73a8637f778673f5c6e91c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 16 Nov 2020 17:26:06 -0300 Subject: [PATCH 046/127] spi: imx: Remove unused .id_table support Since 5.10-rc1 i.MX is a devicetree-only platform and the existing .id_table support in this driver was only useful for old non-devicetree platforms. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201116202606.29888-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 8911465d8f47..7513ef552d79 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1019,33 +1019,6 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .devtype = IMX53_ECSPI, }; -static const struct platform_device_id spi_imx_devtype[] = { - { - .name = "imx1-cspi", - .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data, - }, { - .name = "imx21-cspi", - .driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data, - }, { - .name = "imx27-cspi", - .driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data, - }, { - .name = "imx31-cspi", - .driver_data = (kernel_ulong_t) &imx31_cspi_devtype_data, - }, { - .name = "imx35-cspi", - .driver_data = (kernel_ulong_t) &imx35_cspi_devtype_data, - }, { - .name = "imx51-ecspi", - .driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data, - }, { - .name = "imx53-ecspi", - .driver_data = (kernel_ulong_t) &imx53_ecspi_devtype_data, - }, { - /* sentinel */ - } -}; - static const struct of_device_id spi_imx_dt_ids[] = { { .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, }, { .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, }, @@ -1581,8 +1554,7 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_imx_data *spi_imx; struct resource *res; int ret, irq, spi_drctl; - const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data : - (struct spi_imx_devtype_data *)pdev->id_entry->driver_data; + const struct spi_imx_devtype_data *devtype_data = of_id->data; bool slave_mode; u32 val; @@ -1823,7 +1795,6 @@ static struct platform_driver spi_imx_driver = { .of_match_table = spi_imx_dt_ids, .pm = &imx_spi_pm, }, - .id_table = spi_imx_devtype, .probe = spi_imx_probe, .remove = spi_imx_remove, }; From 03fc41afaa6549baa2dab7a84e1afaf5cadb5b18 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Fri, 6 Nov 2020 09:24:21 +0800 Subject: [PATCH 047/127] spi: mxs: fix reference leak in mxs_spi_probe pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to pm_runtime_put_noidle will result in reference leak in mxs_spi_probe, so we should fix it. Fixes: b7969caf41a1d ("spi: mxs: implement runtime pm") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20201106012421.95420-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 918918a9e049..435309b09227 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -607,6 +607,7 @@ static int mxs_spi_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(ssp->dev); if (ret < 0) { + pm_runtime_put_noidle(ssp->dev); dev_err(ssp->dev, "runtime_get_sync failed\n"); goto out_pm_runtime_disable; } From 440408dbadfe47a615afd0a0a4a402e629be658a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 17:16:02 +0100 Subject: [PATCH 048/127] spi: fix resource leak for drivers without .remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consider an spi driver with a .probe but without a .remove callback (e.g. rtc-ds1347). The function spi_drv_probe() is called to bind a device and so dev_pm_domain_attach() is called. As there is no remove callback spi_drv_remove() isn't called at unbind time however and so calling dev_pm_domain_detach() is missed and the pm domain keeps active. To fix this always use both spi_drv_probe() and spi_drv_remove() and make them handle the respective callback not being set. This has the side effect that for a (hypothetical) driver that has neither .probe nor remove the clk and pm domain setup is done. Fixes: 33cf00e57082 ("spi: attach/detach SPI device to the ACPI power domain") Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119161604.2633521-1-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 22679c8645db..6b1e76371976 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -405,9 +405,11 @@ static int spi_drv_probe(struct device *dev) if (ret) return ret; - ret = sdrv->probe(spi); - if (ret) - dev_pm_domain_detach(dev, true); + if (sdrv->probe) { + ret = sdrv->probe(spi); + if (ret) + dev_pm_domain_detach(dev, true); + } return ret; } @@ -415,9 +417,10 @@ static int spi_drv_probe(struct device *dev) static int spi_drv_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - int ret; + int ret = 0; - ret = sdrv->remove(to_spi_device(dev)); + if (sdrv->remove) + ret = sdrv->remove(to_spi_device(dev)); dev_pm_domain_detach(dev, true); return ret; @@ -442,10 +445,8 @@ int __spi_register_driver(struct module *owner, struct spi_driver *sdrv) { sdrv->driver.owner = owner; sdrv->driver.bus = &spi_bus_type; - if (sdrv->probe) - sdrv->driver.probe = spi_drv_probe; - if (sdrv->remove) - sdrv->driver.remove = spi_drv_remove; + sdrv->driver.probe = spi_drv_probe; + sdrv->driver.remove = spi_drv_remove; if (sdrv->shutdown) sdrv->driver.shutdown = spi_drv_shutdown; return driver_register(&sdrv->driver); From 9db34ee64ce492c7ede3555ed690c8253d9935e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 17:16:03 +0100 Subject: [PATCH 049/127] spi: Use bus_type functions for probe, remove and shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eventual goal is to get rid of the callbacks in struct device_driver. Other than not using driver callbacks there should be no side effect of this patch. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119161604.2633521-2-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6b1e76371976..3aeb54a425a9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -374,16 +374,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) return add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); } -struct bus_type spi_bus_type = { - .name = "spi", - .dev_groups = spi_dev_groups, - .match = spi_match_device, - .uevent = spi_uevent, -}; -EXPORT_SYMBOL_GPL(spi_bus_type); - - -static int spi_drv_probe(struct device *dev) +static int spi_probe(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); struct spi_device *spi = to_spi_device(dev); @@ -414,7 +405,7 @@ static int spi_drv_probe(struct device *dev) return ret; } -static int spi_drv_remove(struct device *dev) +static int spi_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); int ret = 0; @@ -426,13 +417,25 @@ static int spi_drv_remove(struct device *dev) return ret; } -static void spi_drv_shutdown(struct device *dev) +static void spi_shutdown(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - sdrv->shutdown(to_spi_device(dev)); + if (sdrv->shutdown) + sdrv->shutdown(to_spi_device(dev)); } +struct bus_type spi_bus_type = { + .name = "spi", + .dev_groups = spi_dev_groups, + .match = spi_match_device, + .uevent = spi_uevent, + .probe = spi_probe, + .remove = spi_remove, + .shutdown = spi_shutdown, +}; +EXPORT_SYMBOL_GPL(spi_bus_type); + /** * __spi_register_driver - register a SPI driver * @owner: owner module of the driver to register @@ -445,10 +448,6 @@ int __spi_register_driver(struct module *owner, struct spi_driver *sdrv) { sdrv->driver.owner = owner; sdrv->driver.bus = &spi_bus_type; - sdrv->driver.probe = spi_drv_probe; - sdrv->driver.remove = spi_drv_remove; - if (sdrv->shutdown) - sdrv->driver.shutdown = spi_drv_shutdown; return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(__spi_register_driver); From 7795d4757502d8615bf092d628d424300bb31e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Nov 2020 17:16:04 +0100 Subject: [PATCH 050/127] spi: Warn when a driver's remove callback returns an error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver core ignores the return value of struct device_driver::remove (because in general there is nothing that can be done about that). So add a warning when an spi driver returns an error. This simplifies the quest to make struct device_driver::remove return void. A consequent change would be to make struct spi_driver::remove return void, but I'm keeping this quest for later (or someone else). Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20201119161604.2633521-3-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3aeb54a425a9..6ee0f66ddef1 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -408,13 +408,20 @@ static int spi_probe(struct device *dev) static int spi_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - int ret = 0; - if (sdrv->remove) + if (sdrv->remove) { + int ret; + ret = sdrv->remove(to_spi_device(dev)); + if (ret) + dev_warn(dev, + "Failed to unbind driver (%pe), ignoring\n", + ERR_PTR(ret)); + } + dev_pm_domain_detach(dev, true); - return ret; + return 0; } static void spi_shutdown(struct device *dev) From 0f80fcec08e9c50b8d2992cf26495673765ebaba Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:50 +0000 Subject: [PATCH 051/127] dt-bindings: arm: Add support for SCMI Regulators Add devicetree bindings to support regulators based on SCMI Voltage Domain Protocol. Link: https://lore.kernel.org/r/20201119191051.46363-5-cristian.marussi@arm.com Reviewed-by: Rob Herring Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- .../devicetree/bindings/arm/arm,scmi.txt | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt index 55deb68230eb..6e011ca97079 100644 --- a/Documentation/devicetree/bindings/arm/arm,scmi.txt +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt @@ -62,6 +62,29 @@ Required properties: - #power-domain-cells : Should be 1. Contains the device or the power domain ID value used by SCMI commands. +Regulator bindings for the SCMI Regulator based on SCMI Message Protocol +------------------------------------------------------------ +An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain, +and should be always positioned as a root regulator. +It does not support any current operation. + +SCMI Regulators are grouped under a 'regulators' node which in turn is a child +of the SCMI Voltage protocol node inside the desired SCMI instance node. + +This binding uses the common regulator binding[6] but, due to SCMI abstractions, +supports only a subset of its properties as specified below amongst Optional +properties. + +Required properties: + - reg : shall identify an existent SCMI Voltage Domain. + +Optional properties: + - regulator-name + - regulator-min-microvolt / regulator-max-microvolt + - regulator-always-on / regulator-boot-on + - regulator-max-step-microvolt + - regulator-coupled-with / regulator-coupled-max-spread + Sensor bindings for the sensors based on SCMI Message Protocol -------------------------------------------------------------- SCMI provides an API to access the various sensors on the SoC. @@ -105,6 +128,7 @@ Required sub-node properties: [3] Documentation/devicetree/bindings/thermal/thermal*.yaml [4] Documentation/devicetree/bindings/sram/sram.yaml [5] Documentation/devicetree/bindings/reset/reset.txt +[6] Documentation/devicetree/bindings/regulator/regulator.yaml Example: @@ -169,6 +193,25 @@ firmware { reg = <0x16>; #reset-cells = <1>; }; + + scmi_voltage: protocol@17 { + reg = <0x17>; + + regulators { + regulator_devX: regulator@0 { + reg = <0x0>; + regulator-max-microvolt = <3300000>; + }; + + regulator_devY: regulator@9 { + reg = <0x9>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <4200000>; + }; + + ... + }; + }; }; }; From 2add5cacff3531e54c50b0832128299faa9f0563 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:47 +0000 Subject: [PATCH 052/127] firmware: arm_scmi: Add voltage domain management protocol support SCMI v3.0 introduces voltage domain protocol which provides commands to: - Discover the voltage levels supported by a domain - Get the configuration and voltage level of a domain - Set the configuration and voltage level of a domain Let us add support for the same. Link: https://lore.kernel.org/r/20201119191051.46363-2-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/common.h | 1 + drivers/firmware/arm_scmi/driver.c | 2 + drivers/firmware/arm_scmi/voltage.c | 380 ++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 64 +++++ 5 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/voltage.c diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index bc0d54f8e861..6a2ef63306d6 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 65063fa948d4..c0fb45e7c3e8 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf); DECLARE_SCMI_REGISTER_UNREGISTER(power); DECLARE_SCMI_REGISTER_UNREGISTER(reset); DECLARE_SCMI_REGISTER_UNREGISTER(sensors); +DECLARE_SCMI_REGISTER_UNREGISTER(voltage); DECLARE_SCMI_REGISTER_UNREGISTER(system); #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3dfd8b6a0ebf..ada35e63feae 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -946,6 +946,7 @@ static int __init scmi_driver_init(void) scmi_power_register(); scmi_reset_register(); scmi_sensors_register(); + scmi_voltage_register(); scmi_system_register(); return platform_driver_register(&scmi_driver); @@ -961,6 +962,7 @@ static void __exit scmi_driver_exit(void) scmi_power_unregister(); scmi_reset_unregister(); scmi_sensors_unregister(); + scmi_voltage_unregister(); scmi_system_unregister(); platform_driver_unregister(&scmi_driver); diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c new file mode 100644 index 000000000000..e794e4349ae6 --- /dev/null +++ b/drivers/firmware/arm_scmi/voltage.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Voltage Protocol + * + * Copyright (C) 2020 ARM Ltd. + */ + +#include + +#include "common.h" + +#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) +#define REMAINING_LEVELS_MASK GENMASK(31, 16) +#define RETURNED_LEVELS_MASK GENMASK(11, 0) + +enum scmi_voltage_protocol_cmd { + VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + VOLTAGE_DESCRIBE_LEVELS = 0x4, + VOLTAGE_CONFIG_SET = 0x5, + VOLTAGE_CONFIG_GET = 0x6, + VOLTAGE_LEVEL_SET = 0x7, + VOLTAGE_LEVEL_GET = 0x8, +}; + +#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) + +struct scmi_msg_resp_domain_attributes { + __le32 attr; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_cmd_describe_levels { + __le32 domain_id; + __le32 level_index; +}; + +struct scmi_msg_resp_describe_levels { + __le32 flags; +#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f)))) +#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f)))) +#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12)) + __le32 voltage[]; +}; + +struct scmi_msg_cmd_config_set { + __le32 domain_id; + __le32 config; +}; + +struct scmi_msg_cmd_level_set { + __le32 domain_id; + __le32 flags; + __le32 voltage_level; +}; + +struct voltage_info { + unsigned int version; + unsigned int num_domains; + struct scmi_voltage_info *domains; +}; + +static int scmi_protocol_attributes_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret; + struct scmi_xfer *t; + + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t); + if (ret) + return ret; + + ret = scmi_do_xfer(handle, t); + if (!ret) + vinfo->num_domains = + NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf)); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_init_voltage_levels(struct device *dev, + struct scmi_voltage_info *v, + u32 num_returned, u32 num_remaining, + bool segmented) +{ + u32 num_levels; + + num_levels = num_returned + num_remaining; + /* + * segmented levels entries are represented by a single triplet + * returned all in one go. + */ + if (!num_levels || + (segmented && (num_remaining || num_returned != 3))) { + dev_err(dev, + "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n", + num_levels, num_returned, num_remaining, v->id); + return -EINVAL; + } + + v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL); + if (!v->levels_uv) + return -ENOMEM; + + v->num_levels = num_levels; + v->segmented = segmented; + + return 0; +} + +static int scmi_voltage_descriptors_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret, dom; + struct scmi_xfer *td, *tl; + struct device *dev = handle->dev; + struct scmi_msg_resp_domain_attributes *resp_dom; + struct scmi_msg_resp_describe_levels *resp_levels; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le32), + sizeof(*resp_dom), &td); + if (ret) + return ret; + resp_dom = td->rx.buf; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl); + if (ret) + goto outd; + resp_levels = tl->rx.buf; + + for (dom = 0; dom < vinfo->num_domains; dom++) { + u32 desc_index = 0; + u16 num_returned = 0, num_remaining = 0; + struct scmi_msg_cmd_describe_levels *cmd; + struct scmi_voltage_info *v; + + /* Retrieve domain attributes at first ... */ + put_unaligned_le32(dom, td->tx.buf); + ret = scmi_do_xfer(handle, td); + /* Skip domain on comms error */ + if (ret) + continue; + + v = vinfo->domains + dom; + v->id = dom; + v->attributes = le32_to_cpu(resp_dom->attr); + strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); + + cmd = tl->tx.buf; + /* ...then retrieve domain levels descriptions */ + do { + u32 flags; + int cnt; + + cmd->domain_id = cpu_to_le32(v->id); + cmd->level_index = desc_index; + ret = scmi_do_xfer(handle, tl); + if (ret) + break; + + flags = le32_to_cpu(resp_levels->flags); + num_returned = NUM_RETURNED_LEVELS(flags); + num_remaining = NUM_REMAINING_LEVELS(flags); + + /* Allocate space for num_levels if not already done */ + if (!v->num_levels) { + ret = scmi_init_voltage_levels(dev, v, + num_returned, + num_remaining, + SUPPORTS_SEGMENTED_LEVELS(flags)); + if (ret) + break; + } + + if (desc_index + num_returned > v->num_levels) { + dev_err(handle->dev, + "No. of voltage levels can't exceed %d\n", + v->num_levels); + ret = -EINVAL; + break; + } + + for (cnt = 0; cnt < num_returned; cnt++) { + s32 val; + + val = + (s32)le32_to_cpu(resp_levels->voltage[cnt]); + v->levels_uv[desc_index + cnt] = val; + if (val < 0) + v->negative_volts_allowed = true; + } + + desc_index += num_returned; + + scmi_reset_rx_to_maxsz(handle, tl); + /* check both to avoid infinite loop due to buggy fw */ + } while (num_returned && num_remaining); + + if (ret) { + v->num_levels = 0; + devm_kfree(dev, v->levels_uv); + } + + scmi_reset_rx_to_maxsz(handle, td); + } + + scmi_xfer_put(handle, tl); +outd: + scmi_xfer_put(handle, td); + + return ret; +} + +static int __scmi_voltage_get_u32(const struct scmi_handle *handle, + u8 cmd_id, u32 domain_id, u32 *value) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, cmd_id, + SCMI_PROTOCOL_VOLTAGE, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(domain_id, t->tx.buf); + ret = scmi_do_xfer(handle, t); + if (!ret) + *value = get_unaligned_le32(t->rx.buf); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_set(const struct scmi_handle *handle, + u32 domain_id, u32 config) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_config_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->config = cpu_to_le32(config & GENMASK(3, 0)); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_get(const struct scmi_handle *handle, + u32 domain_id, u32 *config) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET, + domain_id, config); +} + +static int scmi_voltage_level_set(const struct scmi_handle *handle, + u32 domain_id, u32 flags, s32 volt_uV) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_level_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->flags = cpu_to_le32(flags); + cmd->voltage_level = cpu_to_le32(volt_uV); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_level_get(const struct scmi_handle *handle, + u32 domain_id, s32 *volt_uV) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET, + domain_id, (u32 *)volt_uV); +} + +static const struct scmi_voltage_info * __must_check +scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains || + !vinfo->domains[domain_id].num_levels) + return NULL; + + return vinfo->domains + domain_id; +} + +static int scmi_voltage_domains_num_get(const struct scmi_handle *handle) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + return vinfo->num_domains; +} + +static struct scmi_voltage_ops voltage_ops = { + .num_domains_get = scmi_voltage_domains_num_get, + .info_get = scmi_voltage_info_get, + .config_set = scmi_voltage_config_set, + .config_get = scmi_voltage_config_get, + .level_set = scmi_voltage_level_set, + .level_get = scmi_voltage_level_get, +}; + +static int scmi_voltage_protocol_init(struct scmi_handle *handle) +{ + int ret; + u32 version; + struct voltage_info *vinfo; + + ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version); + if (ret) + return ret; + + dev_dbg(handle->dev, "Voltage Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL); + if (!vinfo) + return -ENOMEM; + vinfo->version = version; + + ret = scmi_protocol_attributes_get(handle, vinfo); + if (ret) + return ret; + + if (vinfo->num_domains) { + vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains, + sizeof(*vinfo->domains), + GFP_KERNEL); + if (!vinfo->domains) + return -ENOMEM; + ret = scmi_voltage_descriptors_get(handle, vinfo); + if (ret) + return ret; + } else { + dev_warn(handle->dev, "No Voltage domains found.\n"); + } + + handle->voltage_ops = &voltage_ops; + handle->voltage_priv = vinfo; + + return 0; +} + +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9cd312a1ff92..b11ca01e2393 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -209,6 +209,64 @@ struct scmi_reset_ops { int (*deassert)(const struct scmi_handle *handle, u32 domain); }; +/** + * struct scmi_voltage_info - describe one available SCMI Voltage Domain + * + * @id: the domain ID as advertised by the platform + * @segmented: defines the layout of the entries of array @levels_uv. + * - when True the entries are to be interpreted as triplets, + * each defining a segment representing a range of equally + * space voltages: , , + * - when False the entries simply represent a single discrete + * supported voltage level + * @negative_volts_allowed: True if any of the entries of @levels_uv represent + * a negative voltage. + * @attributes: represents Voltage Domain advertised attributes + * @name: name assigned to the Voltage Domain by platform + * @num_levels: number of total entries in @levels_uv. + * @levels_uv: array of entries describing the available voltage levels for + * this domain. + */ +struct scmi_voltage_info { + unsigned int id; + bool segmented; + bool negative_volts_allowed; + unsigned int attributes; + char name[SCMI_MAX_STR_SIZE]; + unsigned int num_levels; +#define SCMI_VOLTAGE_SEGMENT_LOW 0 +#define SCMI_VOLTAGE_SEGMENT_HIGH 1 +#define SCMI_VOLTAGE_SEGMENT_STEP 2 + int *levels_uv; +}; + +/** + * struct scmi_voltage_ops - represents the various operations provided + * by SCMI Voltage Protocol + * + * @num_domains_get: get the count of voltage domains provided by SCMI + * @info_get: get the information of the specified domain + * @config_set: set the config for the specified domain + * @config_get: get the config of the specified domain + * @level_set: set the voltage level for the specified domain + * @level_get: get the voltage level of the specified domain + */ +struct scmi_voltage_ops { + int (*num_domains_get)(const struct scmi_handle *handle); + const struct scmi_voltage_info __must_check *(*info_get) + (const struct scmi_handle *handle, u32 domain_id); + int (*config_set)(const struct scmi_handle *handle, u32 domain_id, + u32 config); +#define SCMI_VOLTAGE_ARCH_STATE_OFF 0x0 +#define SCMI_VOLTAGE_ARCH_STATE_ON 0x7 + int (*config_get)(const struct scmi_handle *handle, u32 domain_id, + u32 *config); + int (*level_set)(const struct scmi_handle *handle, u32 domain_id, + u32 flags, s32 volt_uV); + int (*level_get)(const struct scmi_handle *handle, u32 domain_id, + s32 *volt_uV); +}; + /** * struct scmi_notify_ops - represents notifications' operations provided by * SCMI core @@ -262,6 +320,7 @@ struct scmi_notify_ops { * @clk_ops: pointer to set of clock protocol operations * @sensor_ops: pointer to set of sensor protocol operations * @reset_ops: pointer to set of reset protocol operations + * @voltage_ops: pointer to set of voltage protocol operations * @notify_ops: pointer to set of notifications related operations * @perf_priv: pointer to private data structure specific to performance * protocol(for internal use only) @@ -273,6 +332,8 @@ struct scmi_notify_ops { * protocol(for internal use only) * @reset_priv: pointer to private data structure specific to reset * protocol(for internal use only) + * @voltage_priv: pointer to private data structure specific to voltage + * protocol(for internal use only) * @notify_priv: pointer to private data structure specific to notifications * (for internal use only) */ @@ -284,6 +345,7 @@ struct scmi_handle { const struct scmi_power_ops *power_ops; const struct scmi_sensor_ops *sensor_ops; const struct scmi_reset_ops *reset_ops; + const struct scmi_voltage_ops *voltage_ops; const struct scmi_notify_ops *notify_ops; /* for protocol internal use */ void *perf_priv; @@ -291,6 +353,7 @@ struct scmi_handle { void *power_priv; void *sensor_priv; void *reset_priv; + void *voltage_priv; void *notify_priv; void *system_priv; }; @@ -303,6 +366,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_CLOCK = 0x14, SCMI_PROTOCOL_SENSOR = 0x15, SCMI_PROTOCOL_RESET = 0x16, + SCMI_PROTOCOL_VOLTAGE = 0x17, }; enum scmi_system_events { From ec88381936954a146f260a21bf8466ca07e5c71e Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:48 +0000 Subject: [PATCH 053/127] firmware: arm_scmi: Add support to enumerated SCMI voltage domain device Add SCMI voltage domain device name to the core list of supported protocol devices so that it can be enumerated if the firmware supports it. Link: https://lore.kernel.org/r/20201119191051.46363-3-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ada35e63feae..5392e1fc6b4e 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = { { SCMI_PROTOCOL_CLOCK, { "clocks" },}, { SCMI_PROTOCOL_SENSOR, { "hwmon" },}, { SCMI_PROTOCOL_RESET, { "reset" },}, + { SCMI_PROTOCOL_VOLTAGE, { "regulator" },}, }; static inline void From e7095c35abfc5a5d566941a87434c0fd5ffb570f Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:49 +0000 Subject: [PATCH 054/127] regulator: core: add of_match_full_name boolean flag During regulators registration, if .of_match and .regulators_node are defined as non-null strings in struct regulator_desc the core searches the DT subtree rooted at .regulators_node trying to match, at first, .of_match against the 'regulator-compatible' property and, then, falling back to use the name of the node itself to determine a good match. Property 'regulator-compatible', though, is now deprecated and falling back to match against the node name, works fine only as long as the involved nodes are named in an unique way across the searched subtree; if that's not the case, like when using @ style naming for properties indexed via 'reg' property (as advised by the standard), the above matching mechanism based on the simple common name will lead to multiple matches and the only viable alternative would be to properly define the now deprecated 'regulator-compatible' as the node full name, i.e. @. In order to address this case without using such deprecated binding, define a new boolean flag .of_match_full_name in struct regulator_desc to force the core to match against the node full-name instead of the plain name. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20201119191051.46363-4-cristian.marussi@arm.com Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 8 ++++++-- include/linux/regulator/driver.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 06c0b15fe4c0..564f928eb1db 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev, for_each_available_child_of_node(search, child) { name = of_get_property(child, "regulator-compatible", NULL); - if (!name) - name = child->name; + if (!name) { + if (!desc->of_match_full_name) + name = child->name; + else + name = child->full_name; + } if (!strcmp(desc->of_match, name)) { of_node_put(search); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 11cade73726c..d7c77ee370f3 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -223,6 +223,8 @@ enum regulator_type { * @name: Identifying name for the regulator. * @supply_name: Identifying the regulator supply * @of_match: Name used to identify regulator in DT. + * @of_match_full_name: A flag to indicate that the of_match string, if + * present, should be matched against the node full_name. * @regulators_node: Name of node containing regulator definitions in DT. * @of_parse_cb: Optional callback called only if of_match is present. * Will be called for each regulator parsed from DT, during @@ -314,6 +316,7 @@ struct regulator_desc { const char *name; const char *supply_name; const char *of_match; + bool of_match_full_name; const char *regulators_node; int (*of_parse_cb)(struct device_node *, const struct regulator_desc *, From b52b417ccac4fae5b1f2ec4f1d46eb91e4493dc5 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 20 Nov 2020 12:33:08 -0600 Subject: [PATCH 055/127] regulator: as3722: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a fallthrough pseudo-keyword instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/c0efb81064f71837f19408f65b52d155103ee514.1605896059.git.gustavoars@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/as3722-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 33ca197860b3..7bebf9ce6271 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_FAST: val = as3722_reg_lookup[id].mode_mask; - case REGULATOR_MODE_NORMAL: /* fall down */ + fallthrough; + case REGULATOR_MODE_NORMAL: break; default: return -EINVAL; From e8056bf01080eeb13b0229f3fa4cb25a5a2de6a5 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 23 Nov 2020 20:23:34 +0000 Subject: [PATCH 056/127] dt-bindings: arm: remove optional properties for SCMI Regulators Remove optional properties bindings descriptions for SCMI Regulators. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20201123202336.46701-2-cristian.marussi@arm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/arm/arm,scmi.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt index 6e011ca97079..b5ce5b39bb9c 100644 --- a/Documentation/devicetree/bindings/arm/arm,scmi.txt +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt @@ -71,20 +71,11 @@ It does not support any current operation. SCMI Regulators are grouped under a 'regulators' node which in turn is a child of the SCMI Voltage protocol node inside the desired SCMI instance node. -This binding uses the common regulator binding[6] but, due to SCMI abstractions, -supports only a subset of its properties as specified below amongst Optional -properties. +This binding uses the common regulator binding[6]. Required properties: - reg : shall identify an existent SCMI Voltage Domain. -Optional properties: - - regulator-name - - regulator-min-microvolt / regulator-max-microvolt - - regulator-always-on / regulator-boot-on - - regulator-max-step-microvolt - - regulator-coupled-with / regulator-coupled-max-spread - Sensor bindings for the sensors based on SCMI Message Protocol -------------------------------------------------------------- SCMI provides an API to access the various sensors on the SoC. From 0fbeae70ee7ce98e18a47337cd1f205dd88589e9 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 23 Nov 2020 20:23:36 +0000 Subject: [PATCH 057/127] regulator: add SCMI driver Add a simple regulator based on SCMI Voltage Domain Protocol. Signed-off-by: Cristian Marussi ---- v6 --> v7 - add proper blank lines between semantic blocks - fix return value on error path of scmi_reg_is_enabled() - use generic Failure message on err path of info_get() - fix comment containing apostrophe v3 --> v4 - using of_match_full_name core regulator flag - avoid coccinelle falde complaints about pointer-sized allocations v2 --> v3 - remove multiple linear mappings support - removed duplicated voltage name printout - added a few comments - simplified return path in scmi_reg_set_voltage_sel() v1 --> v2 - removed duplicate regulator naming - removed redundant .get/set_voltage ops: only _sel variants implemented - removed condexpr on fail path to increase readability v0 --> v1 - fixed init_data constraint parsing - fixes for v5.8 (linear_range.h) - fixed commit message content and subject line format - factored out SCMI core specific changes to distinct patch - reworked Kconfig and Makefile to keep proper alphabetic order - fixed SPDX comment style - removed unneeded inline functions - reworked conditionals for legibility - fixed some return paths to properly report SCMI original errors codes - added some more descriptive error messages when fw returns invalid ranges - removed unneeded explicit devm_regulator_unregister from .remove() Link: https://lore.kernel.org/r/20201123202336.46701-4-cristian.marussi@arm.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/scmi-regulator.c | 417 +++++++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 drivers/regulator/scmi-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 005a6036dd38..4c2073a20899 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP and Wolfson Microelectronic Arizona codecs devices. +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OF + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + config REGULATOR_AS3711 tristate "AS3711 PMIC" depends on MFD_AS3711 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6096862a1d60..4e5a06f82de6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o +obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 000000000000..0e8b3caa8146 --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct scmi_regulator { + u32 id; + struct scmi_device *sdev; + struct regulator_dev *rdev; + struct device_node *of_node; + struct regulator_desc desc; + struct regulator_config conf; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->config_get(handle, sreg->id, + &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sreg->desc.name); + return ret; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + s32 delta_uV; + + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in one single + * triplet by the SCMI Voltage Domain protocol support itself. + */ + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + sreg->desc.fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.n_voltages = 1; + sreg->desc.ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + sreg->desc.min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + sreg->desc.linear_min_sel = 0; + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; + sreg->desc.ops = &scmi_reg_linear_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* Discrete non linear levels are mapped to volt_table */ + sreg->desc.n_voltages = vinfo->num_levels; + + if (sreg->desc.n_voltages > 1) { + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; + sreg->desc.ops = &scmi_reg_discrete_ops; + } else { + sreg->desc.fixed_uV = vinfo->levels_uv[0]; + sreg->desc.ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + const struct scmi_handle *handle = sreg->sdev->handle; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + + vinfo = handle->voltage_ops->info_get(handle, sreg->id); + if (!vinfo) { + dev_warn(dev, "Failure to get voltage domain %d\n", + sreg->id); + return -ENODEV; + } + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", + sreg->of_node->full_name); + return -EOPNOTSUPP; + } + + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); + if (!sreg->desc.name) + return -ENOMEM; + + sreg->desc.id = sreg->id; + sreg->desc.type = REGULATOR_VOLTAGE; + sreg->desc.owner = THIS_MODULE; + sreg->desc.of_match_full_name = true; + sreg->desc.of_match = sreg->of_node->full_name; + sreg->desc.regulators_node = "regulators"; + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + if (ret) + return ret; + + /* + * Using the scmi device here to have DT searched from Voltage + * protocol node down. + */ + sreg->conf.dev = dev; + + /* Store for later retrieval via rdev_get_drvdata() */ + sreg->conf.driver_data = sreg; + + return 0; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %s\n", + dom, np->full_name); + return -EINVAL; + } + + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, + sizeof(struct scmi_regulator), + GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + + /* get hold of good nodes */ + of_node_get(np); + rinfo->sregv[dom]->of_node = np; + + dev_dbg(&sdev->dev, + "Found SCMI Regulator entry -- OF node [%d] -> %s\n", + dom, np->full_name); + + return 0; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + + if (!handle || !handle->voltage_ops) + return -ENODEV; + + num_doms = handle->voltage_ops->num_domains_get(handle); + if (num_doms <= 0) { + if (!num_doms) { + dev_err(&sdev->dev, + "number of voltage domains invalid\n"); + num_doms = -EINVAL; + } else { + dev_err(&sdev->dev, + "failed to get voltage domains - err:%d\n", + num_doms); + } + + return num_doms; + } + + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers array for all possible domains */ + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, + sizeof(void *), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI and has a valid associated voltage + * domain. + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + /* Skip empty slots */ + if (!sreg) + continue; + + ret = scmi_regulator_common_init(sreg); + /* Skip invalid voltage domains */ + if (ret) + continue; + + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, + &sreg->conf); + if (IS_ERR(sreg->rdev)) { + sreg->rdev = NULL; + continue; + } + + dev_info(&sdev->dev, + "Regulator %s registered for domain [%d]\n", + sreg->desc.name, sreg->id); + } + + dev_set_drvdata(&sdev->dev, rinfo); + + return 0; +} + +static void scmi_regulator_remove(struct scmi_device *sdev) +{ + int d; + struct scmi_regulator_info *rinfo; + + rinfo = dev_get_drvdata(&sdev->dev); + if (!rinfo) + return; + + for (d = 0; d < rinfo->num_doms; d++) { + if (!rinfo->sregv[d]) + continue; + of_node_put(rinfo->sregv[d]->of_node); + } +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .remove = scmi_regulator_remove, + .id_table = scmi_regulator_id_table, +}; + +module_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi "); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2"); From 2ed6e3bac15242c18bef5af12547a13b25b65ac8 Mon Sep 17 00:00:00 2001 From: Qing Zhang Date: Sat, 21 Nov 2020 11:43:51 +0800 Subject: [PATCH 058/127] spi: amd: Use devm_platform_ioremap_resource() in amd_spi_probe Simplify this function implementation by using a known wrapper function. Signed-off-by: Qing Zhang Link: https://lore.kernel.org/r/1605930231-19448-1-git-send-email-zhangqing@loongson.cn Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 7f629544060d..3cf76096a76d 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -250,7 +250,6 @@ static int amd_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct spi_master *master; struct amd_spi *amd_spi; - struct resource *res; int err = 0; /* Allocate storage for spi_master and driver private data */ @@ -261,9 +260,7 @@ static int amd_spi_probe(struct platform_device *pdev) } amd_spi = spi_master_get_devdata(master); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res); + amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(amd_spi->io_remap_addr)) { err = PTR_ERR(amd_spi->io_remap_addr); dev_err(dev, "error %d ioremap of SPI registers failed\n", err); From e6e9354b58307c120afbdc2719a6093638c37446 Mon Sep 17 00:00:00 2001 From: Philippe Duplessis-Guindon Date: Tue, 24 Nov 2020 08:57:30 -0500 Subject: [PATCH 059/127] regmap: Remove duplicate `type` field from regmap `regcache_sync` trace event I have an error saying that `regcache_sync` has 2 fields named `type` while using libtraceevent. Erase the `int field` type, which is not assigned. This field is introduced by mistake and this commit removes it. Fixes: 593600890110c ("regmap: Add the regcache_sync trace event") Signed-off-by: Philippe Duplessis-Guindon Link: https://lore.kernel.org/r/20201124135730.9185-1-pduplessis@efficios.com Signed-off-by: Mark Brown --- drivers/base/regmap/trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/base/regmap/trace.h b/drivers/base/regmap/trace.h index d4066fa079ab..9abee14df9ee 100644 --- a/drivers/base/regmap/trace.h +++ b/drivers/base/regmap/trace.h @@ -126,7 +126,6 @@ TRACE_EVENT(regcache_sync, __string( name, regmap_name(map) ) __string( status, status ) __string( type, type ) - __field( int, type ) ), TP_fast_assign( From 34c5aa2666db54c4bd330fb2759f6e4d4544ad7a Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 23 Nov 2020 22:23:59 +0000 Subject: [PATCH 060/127] regulator: Kconfig: Fix REGULATOR_QCOM_RPMH dependencies to avoid build error The kernel test robot reported the following build error: All errors (new ones prefixed by >>): xtensa-linux-ld: drivers/regulator/qcom-rpmh-regulator.o: in function `rpmh_regulator_vrm_get_voltage_sel': qcom-rpmh-regulator.c:(.text+0x270): undefined reference to `rpmh_write' xtensa-linux-ld: drivers/regulator/qcom-rpmh-regulator.o: in function `rpmh_regulator_send_request': qcom-rpmh-regulator.c:(.text+0x2f2): undefined reference to `rpmh_write' xtensa-linux-ld: drivers/regulator/qcom-rpmh-regulator.o: in function `rpmh_regulator_vrm_get_voltage_sel': >> qcom-rpmh-regulator.c:(.text+0x274): undefined reference to `rpmh_write_async' xtensa-linux-ld: drivers/regulator/qcom-rpmh-regulator.o: in function `rpmh_regulator_send_request': qcom-rpmh-regulator.c:(.text+0x2fc): undefined reference to `rpmh_write_async' Which is due to REGULATOR_QCOM_RPMH depending on QCOM_RPMH || COMPILE_TEST. The problem is that QOM_RPMH can now be a module, which in that case requires REGULATOR_QCOM_RPMH=m to build. However, if COMPILE_TEST is enabled, REGULATOR_QCOM_RPMH can be set to =y while QCOM_RPMH=m which will cause build failures. The fix here is to add (QCOM_RPMH=n && COMPILE_TEST) to the dependency. Feedback would be appreciated! Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: Maulik Shah Cc: Stephen Boyd Cc: Liam Girdwood Cc: Mark Brown Cc: linux-arm-msm@vger.kernel.org Reported-by: kernel test robot Signed-off-by: John Stultz Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20201123222359.103822-1-john.stultz@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 020a00d6696b..481c7b10133b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -843,7 +843,7 @@ config REGULATOR_QCOM_RPM config REGULATOR_QCOM_RPMH tristate "Qualcomm Technologies, Inc. RPMh regulator driver" - depends on QCOM_RPMH || COMPILE_TEST + depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST) help This driver supports control of PMIC regulators via the RPMh hardware block found on Qualcomm Technologies Inc. SoCs. RPMh regulator From a6f483b2e4415bca7af90346204f93f63b90acdd Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 Nov 2020 14:15:23 +0100 Subject: [PATCH 061/127] spi: Fix potential NULL pointer dereference in spi_shutdown() Shutdown bus function might be called on the unbound device, so add a check if there is a driver before calling its shutdown function. This fixes following kernel panic obserbed during system reboot: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 ... Call trace: spi_shutdown+0x10/0x38 kernel_restart_prepare+0x34/0x40 kernel_restart+0x14/0x88 __do_sys_reboot+0x148/0x248 __arm64_sys_reboot+0x1c/0x28 el0_svc_common.constprop.3+0x74/0x198 do_el0_svc+0x20/0x98 el0_sync_handler+0x140/0x1a8 el0_sync+0x140/0x180 Code: f9403402 d1008041 f100005f 9a9f1021 (f9400c21) ---[ end trace 266c07205a2d632e ]--- Fixes: 9db34ee64ce4 (spi: Use bus_type functions for probe, remove and shutdown) Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20201124131523.32287-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6ee0f66ddef1..1564914930d8 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -426,10 +426,12 @@ static int spi_remove(struct device *dev) static void spi_shutdown(struct device *dev) { - const struct spi_driver *sdrv = to_spi_driver(dev->driver); + if (dev->driver) { + const struct spi_driver *sdrv = to_spi_driver(dev->driver); - if (sdrv->shutdown) - sdrv->shutdown(to_spi_device(dev)); + if (sdrv->shutdown) + sdrv->shutdown(to_spi_device(dev)); + } } struct bus_type spi_bus_type = { From 33f369efbce15e034db4faabeec8502d7d236859 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 24 Nov 2020 19:37:29 +0100 Subject: [PATCH 062/127] regulator: mcp16502-regulator: fix spelling mistake Signed-off-by: Michael Klein Link: https://lore.kernel.org/r/20201124183730.2250690-2-michael@fossekall.de Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/mcp16502-regulator.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt index b8f843fa6092..d86584ed4d93 100644 --- a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt @@ -10,7 +10,7 @@ Required properties: name. The content of each sub-node is defined by the standard binding for regulators; see regulator.txt. -Regualtors of MCP16502 PMIC: +Regulators of MCP16502 PMIC: 1) VDD_IO - Buck (1.2 - 3.7 V) 2) VDD_DDR - Buck (0.6 - 1.85 V) 3) VDD_CORE - Buck (0.6 - 1.85 V) From 459ea85049b01708e364c34deac24b00909c73ed Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 23 Nov 2020 15:02:55 +0800 Subject: [PATCH 063/127] spi: dw: fixed missing resource_size fixed the coccicheck: drivers/spi/spi-dw-bt1.c:220:27-30: ERROR: Missing resource_size with mem. Signed-off-by: Tian Tao Acked-by: Serge Semin Link: https://lore.kernel.org/r/1606114975-31362-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-bt1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index f382dfad7842..da4f4d82617c 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -217,7 +217,7 @@ static int dw_spi_bt1_sys_init(struct platform_device *pdev, if (mem) { dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem); if (!IS_ERR(dwsbt1->map)) { - dwsbt1->map_len = (mem->end - mem->start + 1); + dwsbt1->map_len = resource_size(mem); dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create; dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read; } else { From d9a500b2985b139d7019231ec16e379d2031cb40 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 25 Nov 2020 21:01:28 +0800 Subject: [PATCH 064/127] regmap: sdw: add required header files Explicitly add header files used by regmap SoundWire support. Suggested-by: Guennadi Liakhovetski Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20201125130128.15952-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c index c92d614b4943..c83be26434e7 100644 --- a/drivers/base/regmap/regmap-sdw.c +++ b/drivers/base/regmap/regmap-sdw.c @@ -2,7 +2,9 @@ // Copyright(c) 2015-17 Intel Corporation. #include +#include #include +#include #include #include "internal.h" From 55cca73931c3a08eb74f5ad06e88304af7a292e0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 25 Nov 2020 19:25:47 +0200 Subject: [PATCH 065/127] regulator: core: return zero for selectors lower than linear_min_sel Selectors lower than linear_min_sel should not be considered invalid. Thus return zero in case _regulator_list_voltage(), regulator_list_hardware_vsel() or regulator_list_voltage_table() receives such selectors as argument. Fixes: bdcd1177578c ("regulator: core: validate selector against linear_min_sel") Reported-by: Jon Hunter Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1606325147-606-1-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 10 ++++++---- drivers/regulator/helpers.c | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 85bf278c9378..2d5d5f3bac62 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,9 +2958,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, return rdev->desc->fixed_uV; if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (lock) regulator_lock(rdev); ret = ops->list_voltage(rdev, selector); @@ -3110,9 +3111,10 @@ int regulator_list_hardware_vsel(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 974f1a63993d..f42b394a0c46 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -647,9 +647,10 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, return -EINVAL; } - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; return rdev->desc->volt_table[selector]; } From 1008521b9b4f85d80ac1d80391ac39055c89f736 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 26 Nov 2020 15:00:17 +0530 Subject: [PATCH 066/127] regulator: dt-bindings: Add PMX55 compatibles Add PMX55 compatibles for PMIC found in SDX55 platform Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20201126093018.1085594-1-vkoul@kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/qcom,rpmh-regulator.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt index 97c3e0b7611c..bae558b87686 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt @@ -30,6 +30,7 @@ Supported regulator node names: PMI8998: bob PM6150: smps1 - smps5, ldo1 - ldo19 PM6150L: smps1 - smps8, ldo1 - ldo11, bob + PMX55: smps1 - smps7, ldo1 - ldo16 ======================== First Level Nodes - PMIC @@ -47,6 +48,7 @@ First Level Nodes - PMIC "qcom,pmi8998-rpmh-regulators" "qcom,pm6150-rpmh-regulators" "qcom,pm6150l-rpmh-regulators" + "qcom,pmx55-rpmh-regulators" - qcom,pmic-id Usage: required From 36dd70ceb4d955e6cd3ecd18e78169141aaa23b7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 26 Nov 2020 15:00:18 +0530 Subject: [PATCH 067/127] regulator: qcom-rpmh: Add support for SDX55 Add support from RPMH regulators found in SDX55 platform Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20201126093018.1085594-2-vkoul@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index d488325499a9..e673d48b31a1 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -930,6 +930,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), + {}, +}; + static int rpmh_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1000,6 +1027,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm6150l-rpmh-regulators", .data = pm6150l_vreg_data, }, + { + .compatible = "qcom,pmx55-rpmh-regulators", + .data = pmx55_vreg_data, + }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); From 7218838109fef61cdec988ff728e902d434c9cc5 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 27 Nov 2020 17:46:11 +0300 Subject: [PATCH 068/127] spi: dw-bt1: Fix undefined devm_mux_control_get symbol I mistakenly added the select attributes to the SPI_DW_BT1_DIRMAP config instead of having them defined in SPI_DW_BT1. If the kernel doesn't have the MULTIPLEXER and MUX_MMIO configs manually enabled and the SPI_DW_BT1_DIRMAP config hasn't been selected, Baikal-T1 SPI device will always fail to be probed by the driver. Fix that and the error reported by the test robot: >> ld.lld: error: undefined symbol: devm_mux_control_get >>> referenced by spi-dw-bt1.c >>> spi/spi-dw-bt1.o:(dw_spi_bt1_sys_init) in archive drivers/built-in.a by moving the MULTIPLEXER/MUX_MMIO configs selection to the SPI_DW_BT1 config. Link: https://lore.kernel.org/lkml/202011161745.uYRlekse-lkp@intel.com/ Link: https://lore.kernel.org/linux-spi/20201116040721.8001-1-rdunlap@infradead.org/ Fixes: abf00907538e ("spi: dw: Add Baikal-T1 SPI Controller glue driver") Reported-by: kernel test robot Signed-off-by: Serge Semin Cc: Randy Dunlap Cc: Ramil Zaripov Link: https://lore.kernel.org/r/20201127144612.4204-1-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5cff60de8e83..aadaea052f51 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -255,6 +255,8 @@ config SPI_DW_MMIO config SPI_DW_BT1 tristate "Baikal-T1 SPI driver for DW SPI core" depends on MIPS_BAIKAL_T1 || COMPILE_TEST + select MULTIPLEXER + select MUX_MMIO help Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI controllers. Two of them are pretty much normal: with IRQ, DMA, @@ -268,8 +270,6 @@ config SPI_DW_BT1 config SPI_DW_BT1_DIRMAP bool "Directly mapped Baikal-T1 Boot SPI flash support" depends on SPI_DW_BT1 - select MULTIPLEXER - select MUX_MMIO help Directly mapped SPI flash memory is an interface specific to the Baikal-T1 System Boot Controller. It is a 16MB MMIO region, which From 122541f2b10897b08f7f7e6db5f1eb693e51f0a1 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 27 Nov 2020 16:29:47 +0100 Subject: [PATCH 069/127] spi: fsl: fix use of spisel_boot signal on MPC8309 Commit 0f0581b24bd0 ("spi: fsl: Convert to use CS GPIO descriptors") broke the use of the SPISEL_BOOT signal as a chip select on the MPC8309. pdata->max_chipselect, which becomes master->num_chipselect, must be initialized to take into account the possibility that there's one more chip select in use than the number of GPIO chip selects. Cc: stable@vger.kernel.org # v5.4+ Cc: Christophe Leroy Cc: Linus Walleij Fixes: 0f0581b24bd0 ("spi: fsl: Convert to use CS GPIO descriptors") Signed-off-by: Rasmus Villemoes Link: https://lore.kernel.org/r/20201127152947.376-1-rasmus.villemoes@prevas.dk Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 299e9870cf58..9494257e1c33 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -716,10 +716,11 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) type = fsl_spi_get_type(&ofdev->dev); if (type == TYPE_FSL) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); + bool spisel_boot = false; #if IS_ENABLED(CONFIG_FSL_SOC) struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - bool spisel_boot = of_property_read_bool(np, "fsl,spisel_boot"); + spisel_boot = of_property_read_bool(np, "fsl,spisel_boot"); if (spisel_boot) { pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4); if (!pinfo->immr_spi_cs) @@ -734,10 +735,14 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) * supported on the GRLIB variant. */ ret = gpiod_count(dev, "cs"); - if (ret <= 0) + if (ret < 0) + ret = 0; + if (ret == 0 && !spisel_boot) { pdata->max_chipselect = 1; - else + } else { + pdata->max_chipselect = ret + spisel_boot; pdata->cs_control = fsl_spi_cs_control; + } } ret = of_address_to_resource(np, 0, &mem); From 4b748fb3448b5d39a58cdea55d72e8dcd128f4c6 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Nov 2020 16:53:28 +0530 Subject: [PATCH 070/127] regulator: Add pf8x00 regulator bindings Add NXP PF8100/PF8121A/PF8200 regulators bindings. Signed-off-by: Jagan Teki Link: https://lore.kernel.org/r/20201130112329.104614-1-jagan@amarulasolutions.com Signed-off-by: Mark Brown --- .../regulator/nxp,pf8x00-regulator.yaml | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml new file mode 100644 index 000000000000..a6c259ce9785 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml @@ -0,0 +1,211 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/nxp,pf8x00-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PF8100/PF8121A/PF8200 PMIC regulators + +maintainers: + - Jagan Teki + - Troy Kisky + +description: | + PF8100/PF8121A/PF8200 is a PMIC designed for highperformance consumer + applications. It features seven high efficiency buck converters, four + linear and one vsnvs regulators. It has built-in one time programmable + fuse bank for device configurations. + +properties: + compatible: + enum: + - nxp,pf8x00 + + reg: + maxItems: 1 + + regulators: + type: object + description: | + list of regulators provided by this controller + + patternProperties: + "^ldo[1-4]$": + type: object + $ref: regulator.yaml# + description: + Properties for single LDO regulator. + + properties: + regulator-name: + pattern: "^ldo[1-4]$" + description: + should be "ldo1", ..., "ldo4" + + unevaluatedProperties: false + + "^buck[1-7]$": + type: object + $ref: regulator.yaml# + description: + Properties for single BUCK regulator. + + properties: + regulator-name: + pattern: "^buck[1-7]$" + description: + should be "buck1", ..., "buck7" + + nxp,ilim-ma: + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 2100 + maximum: 4500 + description: + BUCK regulators current limit in mA. + + Listed current limits in mA are, + 2100 (default) + 2600 + 3000 + 4500 + + nxp,phase-shift: + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 45 + maximum: 0 + description: + BUCK regulators phase shift control in degrees. + + Listed phase shift control values in degrees are, + 45 + 90 + 135 + 180 + 225 + 270 + 315 + 0 (default) + + unevaluatedProperties: false + + "^vsnvs$": + type: object + $ref: regulator.yaml# + description: + Properties for single VSNVS regulator. + + properties: + regulator-name: + pattern: "^vsnvs$" + description: + should be "vsnvs" + + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + pmic@8 { + compatible = "nxp,pf8x00"; + reg = <0x08>; + + regulators { + reg_ldo1: ldo1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <1500000>; + }; + + reg_ldo2: ldo2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <1500000>; + }; + + reg_ldo3: ldo3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <1500000>; + }; + + reg_ldo4: ldo4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <1500000>; + }; + + reg_buck1: buck1 { + nxp,ilim-ma = <4500>; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck2: buck2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck3: buck3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck4: buck4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck5: buck5 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck6: buck6 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <400000>; + }; + + reg_buck7: buck7 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + }; + + reg_vsnvs: vsnvs { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + }; + }; + }; + }; From d3795d6321ecaa55d94dc24c3b1e3cce608aabd6 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Nov 2020 16:53:29 +0530 Subject: [PATCH 071/127] regulator: Add NXP PF8X00 regulator driver Add NXP PF8100/PF8121A/PF8200 regulator driver. PF8100/PF8121A/PF8200 is PMIC designed for highperformance consumer applications. It features seven high efficiency buck, four linear and one vsnvs regulators. Tested in Engicam i.Core MX8M Mini SOM platform boards. Signed-off-by: Troy Kisky Signed-off-by: Jagan Teki Link: https://lore.kernel.org/r/20201130112329.104614-2-jagan@amarulasolutions.com Signed-off-by: Mark Brown --- MAINTAINERS | 6 + drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/pf8x00-regulator.c | 496 +++++++++++++++++++++++++++ 4 files changed, 511 insertions(+) create mode 100644 drivers/regulator/pf8x00-regulator.c diff --git a/MAINTAINERS b/MAINTAINERS index 9bff94560b42..131860ff307f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12559,6 +12559,12 @@ S: Maintained F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml F: drivers/gpu/drm/imx/dcss/ +NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER +M: Jagan Teki +S: Maintained +F: Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml +F: drivers/regulator/pf8x00-regulator.c + NXP PTN5150A CC LOGIC AND EXTCON DRIVER M: Krzysztof Kozlowski L: linux-kernel@vger.kernel.org diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 4c2073a20899..a497a3729091 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -812,6 +812,14 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and converters on PCF50633 +config REGULATOR_PF8X00 + tristate "NXP PF8100/PF8121A/PF8200 regulator driver" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the regulators found on the NXP + PF8100/PF8121A/PF8200 PMIC. + config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 4e5a06f82de6..680e539f6579 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o +obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c new file mode 100644 index 000000000000..308c27fa6ea8 --- /dev/null +++ b/drivers/regulator/pf8x00-regulator.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP + * Copyright (C) 2019 Boundary Devices + * Copyright (C) 2020 Amarula Solutions(India) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define PF8X00_DEVICEID 0x00 +#define PF8X00_REVID 0x01 +#define PF8X00_EMREV 0x02 +#define PF8X00_PROGID 0x03 +#define PF8X00_IMS_INT 0x04 +#define PF8X00_IMS_THERM 0x07 +#define PF8X00_SW_MODE_INT 0x0a +#define PF8X00_SW_MODE_MASK 0x0b +#define PF8X00_IMS_SW_ILIM 0x12 +#define PF8X00_IMS_LDO_ILIM 0x15 +#define PF8X00_IMS_SW_UV 0x18 +#define PF8X00_IMS_SW_OV 0x1b +#define PF8X00_IMS_LDO_UV 0x1e +#define PF8X00_IMS_LDO_OV 0x21 +#define PF8X00_IMS_PWRON 0x24 +#define PF8X00_SYS_INT 0x27 +#define PF8X00_HARD_FAULT 0x29 +#define PF8X00_FSOB_FLAGS 0x2a +#define PF8X00_FSOB_SELECT 0x2b +#define PF8X00_ABIST_OV1 0x2c +#define PF8X00_ABIST_OV2 0x2d +#define PF8X00_ABIST_UV1 0x2e +#define PF8X00_ABIST_UV2 0x2f +#define PF8X00_TEST_FLAGS 0x30 +#define PF8X00_ABIST_RUN 0x31 +#define PF8X00_RANDOM_GEN 0x33 +#define PF8X00_RANDOM_CHK 0x34 +#define PF8X00_VMONEN1 0x35 +#define PF8X00_VMONEN2 0x36 +#define PF8X00_CTRL1 0x37 +#define PF8X00_CTRL2 0x38 +#define PF8X00_CTRL3 0x39 +#define PF8X00_PWRUP_CTRL 0x3a +#define PF8X00_RESETBMCU 0x3c +#define PF8X00_PGOOD 0x3d +#define PF8X00_PWRDN_DLY1 0x3e +#define PF8X00_PWRDN_DLY2 0x3f +#define PF8X00_FREQ_CTRL 0x40 +#define PF8X00_COINCELL_CTRL 0x41 +#define PF8X00_PWRON 0x42 +#define PF8X00_WD_CONFIG 0x43 +#define PF8X00_WD_CLEAR 0x44 +#define PF8X00_WD_EXPIRE 0x45 +#define PF8X00_WD_COUNTER 0x46 +#define PF8X00_FAULT_COUNTER 0x47 +#define PF8X00_FSAFE_COUNTER 0x48 +#define PF8X00_FAULT_TIMER 0x49 +#define PF8X00_AMUX 0x4a +#define PF8X00_SW1_CONFIG1 0x4d +#define PF8X00_LDO1_CONFIG1 0x85 +#define PF8X00_VSNVS_CONFIG1 0x9d +#define PF8X00_PAGE_SELECT 0x9f + +/* regulators */ +enum pf8x00_regulators { + PF8X00_LDO1, + PF8X00_LDO2, + PF8X00_LDO3, + PF8X00_LDO4, + PF8X00_BUCK1, + PF8X00_BUCK2, + PF8X00_BUCK3, + PF8X00_BUCK4, + PF8X00_BUCK5, + PF8X00_BUCK6, + PF8X00_BUCK7, + PF8X00_VSNVS, + + PF8X00_MAX_REGULATORS, +}; + +enum pf8x00_buck_states { + SW_CONFIG1, + SW_CONFIG2, + SW_PWRUP, + SW_MODE1, + SW_RUN_VOLT, + SW_STBY_VOLT, +}; +#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1) + +enum pf8x00_ldo_states { + LDO_CONFIG1, + LDO_CONFIG2, + LDO_PWRUP, + LDO_RUN_VOLT, + LDO_STBY_VOLT, +}; +#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1) + +enum swxilim_bits { + SWXILIM_2100_MA, + SWXILIM_2600_MA, + SWXILIM_3000_MA, + SWXILIM_4500_MA, +}; +#define PF8X00_SWXILIM_SHIFT 3 +#define PF8X00_SWXILIM_MASK GENMASK(4, 3) +#define PF8X00_SWXPHASE_MASK GENMASK(2, 0) +#define PF8X00_SWXPHASE_DEFAULT 0 +#define PF8X00_SWXPHASE_SHIFT 7 + +enum pf8x00_devid { + PF8100 = 0x0, + PF8121A = BIT(1), + PF8200 = BIT(3), +}; +#define PF8X00_FAM BIT(6) +#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) +#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) + +struct pf8x00_regulator { + struct regulator_desc desc; + u8 ilim; + u8 phase_shift; +}; + +struct pf8x00_chip { + struct regmap *regmap; + struct device *dev; +}; + +static const struct regmap_config pf8x00_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PF8X00_PAGE_SELECT, + .cache_type = REGCACHE_RBTREE, +}; + +/* VLDOx output: 1.5V to 5.0V */ +static const int pf8x00_ldo_voltages[] = { + 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000, + 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, +}; + +#define SWV(i) (6250 * i + 400000) +#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \ + SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7) + +/* Output: 0.4V to 1.8V */ +static const int pf8x00_sw1_to_6_voltages[] = { + SWV_LINE(0), + SWV_LINE(1), + SWV_LINE(2), + SWV_LINE(3), + SWV_LINE(4), + SWV_LINE(5), + SWV_LINE(6), + SWV_LINE(7), + SWV_LINE(8), + SWV_LINE(9), + SWV_LINE(10), + SWV_LINE(11), + SWV_LINE(12), + SWV_LINE(13), + SWV_LINE(14), + SWV_LINE(15), + SWV_LINE(16), + SWV_LINE(17), + SWV_LINE(18), + SWV_LINE(19), + SWV_LINE(20), + SWV_LINE(21), + 1500000, 1800000, +}; + +/* Output: 1.0V to 4.1V */ +static const int pf8x00_sw7_voltages[] = { + 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000, + 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000, + 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, + 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000, +}; + +/* Output: 1.8V, 3.0V, or 3.3V */ +static const int pf8x00_vsnvs_voltages[] = { + 0, 1800000, 3000000, 3300000, +}; + +static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc) +{ + return container_of(desc, struct pf8x00_regulator, desc); +} + +static void swxilim_select(const struct regulator_desc *desc, int ilim) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + u8 ilim_sel; + + switch (ilim) { + case 2100: + ilim_sel = SWXILIM_2100_MA; + break; + case 2600: + ilim_sel = SWXILIM_2600_MA; + break; + case 3000: + ilim_sel = SWXILIM_3000_MA; + break; + case 4500: + ilim_sel = SWXILIM_4500_MA; + break; + default: + ilim_sel = SWXILIM_2100_MA; + break; + } + + data->ilim = ilim_sel; +} + +static int pf8x00_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + struct pf8x00_chip *chip = config->driver_data; + int phase; + int val; + int ret; + + ret = of_property_read_u32(np, "nxp,ilim-ma", &val); + if (ret) + dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n", + desc->id - PF8X00_LDO4); + + swxilim_select(desc, val); + + ret = of_property_read_u32(np, "nxp,phase-shift", &val); + if (ret) { + dev_dbg(chip->dev, + "unspecified phase-shift for BUCK%d, use 0 degrees\n", + desc->id - PF8X00_LDO4); + val = PF8X00_SWXPHASE_DEFAULT; + } + + phase = val / 45; + if ((phase * 45) != val) { + dev_warn(config->dev, + "invalid phase_shift %d for BUCK%d, use 0 degrees\n", + (phase * 45), desc->id - PF8X00_LDO4); + phase = PF8X00_SWXPHASE_SHIFT; + } + + data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT; + + return 0; +} + +static const struct regulator_ops pf8x00_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_vsnvs_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +#define PF8X00LDO(_id, _name, base, voltages) \ + [PF8X00_LDO ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_LDO ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + LDO_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + LDO_CONFIG2, \ + .enable_val = 0x2, \ + .disable_val = 0x0, \ + .enable_mask = 2, \ + }, \ + } + +#define PF8X00BUCK(_id, _name, base, voltages) \ + [PF8X00_BUCK ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .of_parse_cb = pf8x00_of_parse_cb, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_BUCK ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + SW_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + SW_MODE1, \ + .enable_val = 0x3, \ + .disable_val = 0x0, \ + .enable_mask = 0x3, \ + .enable_time = 500, \ + }, \ + } + +#define PF8X00VSNVS(_name, base, voltages) \ + [PF8X00_VSNVS] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_vsnvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_VSNVS, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + }, \ + } + +static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = { + PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages), + PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages), + PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages), + PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages), + PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), + PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), +}; + +static int pf8x00_identify(struct pf8x00_chip *chip) +{ + unsigned int value; + u8 dev_fam, dev_id; + const char *name = NULL; + int ret; + + ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value); + if (ret) { + dev_err(chip->dev, "failed to read chip family\n"); + return ret; + } + + dev_fam = value & PF8X00_DEVICE_FAM_MASK; + switch (dev_fam) { + case PF8X00_FAM: + break; + default: + dev_err(chip->dev, + "Chip 0x%x is not from PF8X00 family\n", dev_fam); + return ret; + } + + dev_id = value & PF8X00_DEVICE_ID_MASK; + switch (dev_id) { + case PF8100: + name = "PF8100"; + break; + case PF8121A: + name = "PF8121A"; + break; + case PF8200: + name = "PF8100"; + break; + default: + dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id); + return -ENODEV; + } + + dev_info(chip->dev, "%s PMIC found.\n", name); + + return 0; +} + +static int pf8x00_i2c_probe(struct i2c_client *client) +{ + struct regulator_config config = { NULL, }; + struct pf8x00_chip *chip; + int id; + int ret; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->dev = &client->dev; + + chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pf8x00_identify(chip); + if (ret) + return ret; + + for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) { + struct pf8x00_regulator *data = &pf8x00_regulators_data[id]; + struct regulator_dev *rdev; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + rdev = devm_regulator_register(&client->dev, &data->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&client->dev, + "failed to register %s regulator\n", data->desc.name); + return PTR_ERR(rdev); + } + + if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) { + u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXPHASE_MASK, + data->phase_shift); + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXILIM_MASK, + data->ilim << PF8X00_SWXILIM_SHIFT); + } + } + + return 0; +} + +static const struct of_device_id pf8x00_dt_ids[] = { + { .compatible = "nxp,pf8x00",}, + { } +}; +MODULE_DEVICE_TABLE(of, pf8x00_dt_ids); + +static const struct i2c_device_id pf8x00_i2c_id[] = { + { "pf8x00", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id); + +static struct i2c_driver pf8x00_regulator_driver = { + .id_table = pf8x00_i2c_id, + .driver = { + .name = "pf8x00", + .of_match_table = pf8x00_dt_ids, + }, + .probe_new = pf8x00_i2c_probe, +}; +module_i2c_driver(pf8x00_regulator_driver); + +MODULE_AUTHOR("Jagan Teki "); +MODULE_AUTHOR("Troy Kisky "); +MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC"); +MODULE_LICENSE("GPL v2"); From 2a56e9c713fe38106cc62d928d4d73963a8548d5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Nov 2020 17:33:48 +0000 Subject: [PATCH 072/127] ASoC: rt715: Fix build This reverts 6f4a038b99677 (ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver) due to build breakage. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 7 - sound/soc/codecs/Makefile | 2 - sound/soc/codecs/rt715-sdca-sdw.c | 278 --------- sound/soc/codecs/rt715-sdca-sdw.h | 170 ------ sound/soc/codecs/rt715-sdca.c | 936 ------------------------------ sound/soc/codecs/rt715-sdca.h | 124 ---- 6 files changed, 1517 deletions(-) delete mode 100644 sound/soc/codecs/rt715-sdca-sdw.c delete mode 100644 sound/soc/codecs/rt715-sdca-sdw.h delete mode 100644 sound/soc/codecs/rt715-sdca.c delete mode 100644 sound/soc/codecs/rt715-sdca.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e7797f08e057..34c6dd04b85a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -177,7 +177,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT700_SDW imply SND_SOC_RT711_SDW imply SND_SOC_RT715_SDW - imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_SGTL5000 imply SND_SOC_SI476X @@ -1217,12 +1216,6 @@ config SND_SOC_RT715_SDW select SND_SOC_RT715 select REGMAP_SOUNDWIRE -config SND_SOC_RT715_SDCA_SDW - tristate "Realtek RT715 SDCA Codec - SDW" - depends on SOUNDWIRE - select REGMAP_SOUNDWIRE - select REGMAP_SOUNDWIRE_MBQ - #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b1683403afb3..11ce98c25d6c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -188,7 +188,6 @@ snd-soc-rt5682-i2c-objs := rt5682-i2c.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o -snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -499,7 +498,6 @@ obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o -obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c deleted file mode 100644 index 889b6b3b0009..000000000000 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver -// -// Copyright(c) 2020 Realtek Semiconductor Corp. -// -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "rt715-sdca.h" -#include "rt715-sdca-sdw.h" - -static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x201a ... 0x2027: - case 0x2029 ... 0x202a: - case 0x202d ... 0x2034: - case 0x2200 ... 0x2204: - case 0x2206 ... 0x2212: - case 0x2230 ... 0x2239: - case 0x2f5b: - case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): - return true; - default: - return false; - } -} - -static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x201b: - case 0x201c: - case 0x201d: - case 0x201f: - case 0x2021: - case 0x2023: - case 0x2230: - case 0x202d ... 0x202f: /* BRA */ - case 0x2200 ... 0x2212: /* i2c debug */ - case 0x2f07: - case 0x2f1b ... 0x2f1e: - case 0x2f30 ... 0x2f34: - case 0x2f50 ... 0x2f51: - case 0x2f53 ... 0x2f59: - case 0x2f5c ... 0x2f5f: - case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */ - return true; - default: - return false; - } -} - -static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x2000000: - case 0x200002b: - case 0x2000036: - case 0x2000037: - case 0x2000039: - case 0x6100000: - return true; - default: - return false; - } -} - -static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x2000000: - return true; - default: - return false; - } -} - -static const struct regmap_config rt715_sdca_regmap = { - .reg_bits = 32, - .val_bits = 8, - .readable_reg = rt715_sdca_readable_register, - .volatile_reg = rt715_sdca_volatile_register, - .max_register = 0x43ffffff, - .reg_defaults = rt715_reg_defaults_sdca, - .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, - .use_single_read = true, - .use_single_write = true, -}; - -static const struct regmap_config rt715_sdca_mbq_regmap = { - .name = "sdw-mbq", - .reg_bits = 32, - .val_bits = 16, - .readable_reg = rt715_sdca_mbq_readable_register, - .volatile_reg = rt715_sdca_mbq_volatile_register, - .max_register = 0x43ffffff, - .reg_defaults = rt715_mbq_reg_defaults_sdca, - .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, - .use_single_read = true, - .use_single_write = true, -}; - -static int rt715_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - - /* Update the status */ - rt715->status = status; - - /* - * Perform initialization only if slave status is present and - * hw_init flag is false - */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) - return 0; - - /* perform I/O transfers required for Slave initialization */ - return rt715_io_init(&slave->dev, slave); -} - -static int rt715_read_prop(struct sdw_slave *slave) -{ - struct sdw_slave_prop *prop = &slave->prop; - int nval, i; - u32 bit; - unsigned long addr; - struct sdw_dpn_prop *dpn; - - prop->paging_support = true; - - /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x50;/* BITMAP: 01010000 */ - prop->sink_ports = 0x0; /* BITMAP: 00000000 */ - - nval = hweight32(prop->source_ports); - prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), - GFP_KERNEL); - if (!prop->src_dpn_prop) - return -ENOMEM; - - dpn = prop->src_dpn_prop; - i = 0; - addr = prop->source_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].simple_ch_prep_sm = true; - dpn[i].ch_prep_timeout = 10; - i++; - } - - /* set the timeout values */ - prop->clk_stop_timeout = 20; - - return 0; -} - -static struct sdw_slave_ops rt715_sdca_slave_ops = { - .read_prop = rt715_read_prop, - .update_status = rt715_update_status, -}; - -static int rt715_sdca_sdw_probe(struct sdw_slave *slave, - const struct sdw_device_id *id) -{ - struct regmap *mbq_regmap, *regmap; - - slave->ops = &rt715_sdca_slave_ops; - - /* Regmap Initialization */ - mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); - if (!mbq_regmap) - return -EINVAL; - - regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap); - if (!regmap) - return -EINVAL; - - return rt715_init(&slave->dev, mbq_regmap, regmap, slave); -} - -static const struct sdw_device_id rt715_sdca_id[] = { - SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0), - SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0), - {}, -}; -MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); - -static int __maybe_unused rt715_dev_suspend(struct device *dev) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - - if (!rt715->hw_init) - return 0; - - regcache_cache_only(rt715->regmap, true); - regcache_mark_dirty(rt715->regmap); - regcache_cache_only(rt715->mbq_regmap, true); - regcache_mark_dirty(rt715->mbq_regmap); - - return 0; -} - -#define RT715_PROBE_TIMEOUT 2000 - -static int __maybe_unused rt715_dev_resume(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - unsigned long time; - - if (!rt715->hw_init) - return 0; - - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->enumeration_complete, - msecs_to_jiffies(RT715_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); - return -ETIMEDOUT; - } - -regmap_sync: - slave->unattach_request = 0; - regcache_cache_only(rt715->regmap, false); - regcache_sync_region(rt715->regmap, - SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, - CH_00), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); - regcache_cache_only(rt715->mbq_regmap, false); - regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff); - regcache_sync_region(rt715->mbq_regmap, - SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, - CH_00), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); - - return 0; -} - -static const struct dev_pm_ops rt715_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) - SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) -}; - -static struct sdw_driver rt715_sdw_driver = { - .driver = { - .name = "rt715-sdca", - .owner = THIS_MODULE, - .pm = &rt715_pm, - }, - .probe = rt715_sdca_sdw_probe, - .ops = &rt715_sdca_slave_ops, - .id_table = rt715_sdca_id, -}; -module_sdw_driver(rt715_sdw_driver); - -MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA"); -MODULE_AUTHOR("Jack Yu "); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h deleted file mode 100644 index cd365bb60747..000000000000 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header - * - * Copyright(c) 2020 Realtek Semiconductor Corp. - */ - -#ifndef __RT715_SDW_SDCA_H__ -#define __RT715_SDW_SDCA_H__ - -#include - -static const struct reg_default rt715_reg_defaults_sdca[] = { - { 0x201a, 0x00 }, - { 0x201e, 0x00 }, - { 0x2020, 0x00 }, - { 0x2021, 0x00 }, - { 0x2022, 0x00 }, - { 0x2023, 0x00 }, - { 0x2024, 0x00 }, - { 0x2025, 0x01 }, - { 0x2026, 0x00 }, - { 0x2027, 0x00 }, - { 0x2029, 0x00 }, - { 0x202a, 0x00 }, - { 0x202d, 0x00 }, - { 0x202e, 0x00 }, - { 0x202f, 0x00 }, - { 0x2030, 0x00 }, - { 0x2031, 0x00 }, - { 0x2032, 0x00 }, - { 0x2033, 0x00 }, - { 0x2034, 0x00 }, - { 0x2230, 0x00 }, - { 0x2231, 0x2f }, - { 0x2232, 0x80 }, - { 0x2233, 0x00 }, - { 0x2234, 0x00 }, - { 0x2235, 0x00 }, - { 0x2236, 0x00 }, - { 0x2237, 0x00 }, - { 0x2238, 0x00 }, - { 0x2239, 0x00 }, - { 0x2f01, 0x00 }, - { 0x2f02, 0x09 }, - { 0x2f03, 0x0b }, - { 0x2f04, 0x00 }, - { 0x2f05, 0x0e }, - { 0x2f06, 0x01 }, - { 0x2f08, 0x00 }, - { 0x2f09, 0x00 }, - { 0x2f0a, 0x00 }, - { 0x2f0b, 0x00 }, - { 0x2f0c, 0x00 }, - { 0x2f0d, 0x00 }, - { 0x2f0e, 0x12 }, - { 0x2f0f, 0x00 }, - { 0x2f10, 0x00 }, - { 0x2f11, 0x00 }, - { 0x2f12, 0x00 }, - { 0x2f13, 0x00 }, - { 0x2f14, 0x00 }, - { 0x2f15, 0x00 }, - { 0x2f16, 0x00 }, - { 0x2f17, 0x00 }, - { 0x2f18, 0x00 }, - { 0x2f19, 0x03 }, - { 0x2f1a, 0x00 }, - { 0x2f1f, 0x10 }, - { 0x2f20, 0x00 }, - { 0x2f21, 0x00 }, - { 0x2f22, 0x00 }, - { 0x2f23, 0x00 }, - { 0x2f24, 0x00 }, - { 0x2f25, 0x00 }, - { 0x2f52, 0x01 }, - { 0x2f5a, 0x02 }, - { 0x2f5b, 0x05 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, -}; - -static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { - { 0x200002b, 0x0420 }, - { 0x2000036, 0x0000 }, - { 0x2000037, 0x0000 }, - { 0x2000039, 0xaa81 }, - { 0x6100000, 0x0100 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, -}; -#endif /* __RT715_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c deleted file mode 100644 index b843e47eb25b..000000000000 --- a/sound/soc/codecs/rt715-sdca.c +++ /dev/null @@ -1,936 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// rt715-sdca.c -- rt715 ALSA SoC audio driver -// -// Copyright(c) 2020 Realtek Semiconductor Corp. -// -// -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rt715-sdca.h" - -static int rt715_index_write(struct rt715_sdca_priv *rt715, unsigned int nid, - unsigned int reg, unsigned int value) -{ - struct regmap *regmap = rt715->mbq_regmap; - unsigned int addr; - int ret; - - addr = (nid << 20) | reg; - - ret = regmap_write(regmap, addr, value); - if (ret < 0) - dev_err(&rt715->slave->dev, - "Failed to set private value: %08x <= %04x %d\n", ret, addr, - value); - - return ret; -} - -static int rt715_index_read(struct rt715_sdca_priv *rt715, - unsigned int nid, unsigned int reg, unsigned int *value) -{ - struct regmap *regmap = rt715->mbq_regmap; - unsigned int addr; - int ret; - - addr = (nid << 20) | reg; - - ret = regmap_read(regmap, addr, value); - if (ret < 0) - dev_err(&rt715->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); - - return ret; -} - -static int rt715_index_update_bits(struct rt715_sdca_priv *rt715, - unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) -{ - unsigned int tmp; - int ret; - - ret = rt715_index_read(rt715, nid, reg, &tmp); - if (ret < 0) - return ret; - - set_mask_bits(&tmp, mask, val); - - return rt715_index_write(rt715, nid, reg, tmp); -} - -/* SDCA Volume/Boost control */ -static int rt715_set_amp_gain_put_sdca(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val_l, val_r, gain_l_val, gain_r_val; - int ret; - - /* control value to 2s complement */ - /* L channel */ - gain_l_val = ucontrol->value.integer.value[0]; - if (gain_l_val > mc->max) - gain_l_val = mc->max; - val_l = gain_l_val; - - if (mc->shift == 8) { - gain_l_val = (gain_l_val * 10) << mc->shift; - } else { - gain_l_val = - ((abs(gain_l_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; - if (val_l <= mc->shift) { - gain_l_val = ~gain_l_val; - gain_l_val += 1; - } - gain_l_val &= 0xffff; - } - - /* R channel */ - gain_r_val = ucontrol->value.integer.value[1]; - if (gain_r_val > mc->max) - gain_r_val = mc->max; - val_r = gain_r_val; - - if (mc->shift == 8) { - gain_r_val = (gain_r_val * 10) << mc->shift; - } else { - gain_r_val = - ((abs(gain_r_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; - if (val_r <= mc->shift) { - gain_r_val = ~gain_r_val; - gain_r_val += 1; - } - gain_r_val &= 0xffff; - } - - /* Lch*/ - ret = regmap_write(rt715->mbq_regmap, mc->reg, gain_l_val); - if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->reg, - gain_l_val); - return ret; - } - /* Rch */ - ret = regmap_write(rt715->mbq_regmap, mc->rreg, gain_r_val); - if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->rreg, - gain_r_val); - return ret; - } - - return 0; -} - -static int rt715_set_amp_gain_get_sdca(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val_l, val_r, ctl_l, ctl_r, neg_flag = 0; - int ret; - - ret = regmap_read(rt715->mbq_regmap, mc->reg, &val_l); - if (ret < 0) - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->reg, ret); - ret = regmap_read(rt715->mbq_regmap, mc->rreg, &val_r); - if (ret < 0) - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->rreg, - ret); - - /* L channel */ - if (mc->shift == 8) { - ctl_l = (val_l >> mc->shift) / 10; - } else { - ctl_l = val_l; - if (ctl_l & BIT(15)) { - ctl_l = ~(val_l - 1) & 0xffff; - neg_flag = 1; - } - ctl_l *= 1000; - ctl_l >>= 8; - if (neg_flag) - ctl_l = mc->shift - ctl_l / RT715_SDCA_DB_STEP; - else - ctl_l = mc->shift + ctl_l / RT715_SDCA_DB_STEP; - } - - neg_flag = 0; - /* R channel */ - if (mc->shift == 8) { - ctl_r = (val_r >> mc->shift) / 10; - } else { - ctl_r = val_r; - if (ctl_r & BIT(15)) { - ctl_r = ~(val_r - 1) & 0xffff; - neg_flag = 1; - } - ctl_r *= 1000; - ctl_r >>= 8; - if (neg_flag) - ctl_r = mc->shift - ctl_r / RT715_SDCA_DB_STEP; - else - ctl_r = mc->shift + ctl_r / RT715_SDCA_DB_STEP; - } - - ucontrol->value.integer.value[0] = ctl_l; - ucontrol->value.integer.value[1] = ctl_r; - - return 0; -} - -static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); -static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); - -#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ - xmax, xinvert) } - -static const struct snd_kcontrol_new rt715_snd_controls_sdca[] = { - /* Capture switch */ - SOC_DOUBLE_R("FU0A Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU02 1_2 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU02 3_4 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), - 0, 1, 1), - SOC_DOUBLE_R("FU06 1_2 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU06 3_4 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), - 0, 1, 1), - /* Volume Control */ - SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU02 1_2 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU02 3_4 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_04), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU06 1_2 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_02), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU06 3_4 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_04), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - /* MIC Boost Control */ - SOC_DOUBLE_R_EXT_TLV("FU0E 1_2 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_02), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 3_4 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_04), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 5_6 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_05), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_06), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 7_8 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_07), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_08), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 1_2 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_02), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 3_4 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_04), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 5_6 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_05), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_06), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 7_8 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_07), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_08), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), -}; - -static int rt715_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val, mask_sft; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 23 Mux")) - mask_sft = 8; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, &val); - val = (val >> mask_sft) & 0xf; - - /* - * The first two indices of ADC Mux 24/25 are routed to the same - * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. - * To have a unique set of inputs, we skip the index1 of the muxes. - */ - if ((strstr(ucontrol->id.name, "ADC 24 Mux") || - strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0) - val -= 1; - ucontrol->value.enumerated.item[0] = val; - - return 0; -} - -static int rt715_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = - snd_soc_dapm_kcontrol_dapm(kcontrol); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val, val2 = 0, change, mask_sft; - - if (item[0] >= e->items) - return -EINVAL; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 23 Mux")) - mask_sft = 8; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - /* Verb ID = 0x701h, nid = e->reg */ - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - - rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, &val2); - val2 = (val2 >> mask_sft) & 0xf; - - change = val != val2; - - if (change) - rt715_index_update_bits(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft); - - snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); - - return change; -} - -static const char * const adc_22_23_mux_text[] = { - "MIC1", - "MIC2", - "LINE1", - "LINE2", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -/* - * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and - * 1 will be connected to the same dmic source, therefore we skip index 1 to - * avoid misunderstanding on usage of dapm routing. - */ -static int rt715_adc_24_25_values[] = { - 0, - 2, - 3, - 4, - 5, -}; - -static const char * const adc_24_mux_text[] = { - "MIC2", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -static const char * const adc_25_mux_text[] = { - "MIC1", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0, - adc_22_23_mux_text); - -static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0, - adc_22_23_mux_text); - -static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, - SND_SOC_NOPM, 0, 0xf, - adc_24_mux_text, rt715_adc_24_25_values); -static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, - SND_SOC_NOPM, 0, 0xf, - adc_25_mux_text, rt715_adc_24_25_values); - -static const struct snd_kcontrol_new rt715_adc22_mux = - SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc23_mux = - SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc24_mux = - SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc25_mux = - SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, - rt715_mux_get, rt715_mux_put); - -static int rt715_pde23_24_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = - snd_soc_dapm_to_component(w->dapm); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, - RT715_SDCA_REQ_POW_CTRL, - CH_00), 0x00); - break; - case SND_SOC_DAPM_PRE_PMD: - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, - RT715_SDCA_REQ_POW_CTRL, - CH_00), 0x03); - break; - } - return 0; -} - -static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("DMIC1"), - SND_SOC_DAPM_INPUT("DMIC2"), - SND_SOC_DAPM_INPUT("DMIC3"), - SND_SOC_DAPM_INPUT("DMIC4"), - SND_SOC_DAPM_INPUT("MIC1"), - SND_SOC_DAPM_INPUT("MIC2"), - SND_SOC_DAPM_INPUT("LINE1"), - SND_SOC_DAPM_INPUT("LINE2"), - - SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0, - rt715_pde23_24_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc22_mux), - SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc23_mux), - SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc24_mux), - SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc25_mux), - SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), -}; - -static const struct snd_soc_dapm_route rt715_audio_map[] = { - {"DP6TX", NULL, "ADC 09"}, - {"DP6TX", NULL, "ADC 08"}, - {"DP4TX", NULL, "ADC 07"}, - {"DP4TX", NULL, "ADC 27"}, - {"DP4TX", NULL, "ADC 09"}, - {"DP4TX", NULL, "ADC 08"}, - - {"LINE1", NULL, "PDE23_24"}, - {"LINE2", NULL, "PDE23_24"}, - {"MIC1", NULL, "PDE23_24"}, - {"MIC2", NULL, "PDE23_24"}, - {"DMIC1", NULL, "PDE23_24"}, - {"DMIC2", NULL, "PDE23_24"}, - {"DMIC3", NULL, "PDE23_24"}, - {"DMIC4", NULL, "PDE23_24"}, - - {"ADC 09", NULL, "ADC 22 Mux"}, - {"ADC 08", NULL, "ADC 23 Mux"}, - {"ADC 07", NULL, "ADC 24 Mux"}, - {"ADC 27", NULL, "ADC 25 Mux"}, - {"ADC 22 Mux", "MIC1", "MIC1"}, - {"ADC 22 Mux", "MIC2", "MIC2"}, - {"ADC 22 Mux", "LINE1", "LINE1"}, - {"ADC 22 Mux", "LINE2", "LINE2"}, - {"ADC 22 Mux", "DMIC1", "DMIC1"}, - {"ADC 22 Mux", "DMIC2", "DMIC2"}, - {"ADC 22 Mux", "DMIC3", "DMIC3"}, - {"ADC 22 Mux", "DMIC4", "DMIC4"}, - {"ADC 23 Mux", "MIC1", "MIC1"}, - {"ADC 23 Mux", "MIC2", "MIC2"}, - {"ADC 23 Mux", "LINE1", "LINE1"}, - {"ADC 23 Mux", "LINE2", "LINE2"}, - {"ADC 23 Mux", "DMIC1", "DMIC1"}, - {"ADC 23 Mux", "DMIC2", "DMIC2"}, - {"ADC 23 Mux", "DMIC3", "DMIC3"}, - {"ADC 23 Mux", "DMIC4", "DMIC4"}, - {"ADC 24 Mux", "MIC2", "MIC2"}, - {"ADC 24 Mux", "DMIC1", "DMIC1"}, - {"ADC 24 Mux", "DMIC2", "DMIC2"}, - {"ADC 24 Mux", "DMIC3", "DMIC3"}, - {"ADC 24 Mux", "DMIC4", "DMIC4"}, - {"ADC 25 Mux", "MIC1", "MIC1"}, - {"ADC 25 Mux", "DMIC1", "DMIC1"}, - {"ADC 25 Mux", "DMIC2", "DMIC2"}, - {"ADC 25 Mux", "DMIC3", "DMIC3"}, - {"ADC 25 Mux", "DMIC4", "DMIC4"}, -}; - -static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = { - .controls = rt715_snd_controls_sdca, - .num_controls = ARRAY_SIZE(rt715_snd_controls_sdca), - .dapm_widgets = rt715_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), - .dapm_routes = rt715_audio_map, - .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), -}; - -static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, - int direction) -{ - struct rt715_sdw_stream_data *stream; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - return -ENOMEM; - - stream->sdw_stream = sdw_stream; - - /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; - - return 0; -} - -static void rt715_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - -{ - struct rt715_sdw_stream_data *stream; - - stream = snd_soc_dai_get_dma_data(dai, substream); - if (!stream) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(stream); -} - -static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; - struct sdw_port_config port_config; - enum sdw_data_direction direction; - struct rt715_sdw_stream_data *stream; - int retval, port, num_channels; - unsigned int val; - - stream = snd_soc_dai_get_dma_data(dai, substream); - - if (!stream) - return -EINVAL; - - if (!rt715->slave) - return -EINVAL; - - switch (dai->id) { - case RT715_AIF1: - direction = SDW_DATA_DIR_TX; - port = 6; - rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, - 0xa500); - break; - case RT715_AIF2: - direction = SDW_DATA_DIR_TX; - port = 4; - rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, - 0xaf00); - break; - default: - dev_err(component->dev, "Invalid DAI id %d\n", dai->id); - return -EINVAL; - } - - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); - port_config.num = port; - - retval = sdw_stream_add_slave(rt715->slave, &stream_config, - &port_config, 1, stream->sdw_stream); - if (retval) { - dev_err(component->dev, "Unable to configure port, retval:%d\n", - retval); - return retval; - } - - switch (params_rate(params)) { - case 8000: - val = 0x1; - break; - case 11025: - val = 0x2; - break; - case 12000: - val = 0x3; - break; - case 16000: - val = 0x4; - break; - case 22050: - val = 0x5; - break; - case 24000: - val = 0x6; - break; - case 32000: - val = 0x7; - break; - case 44100: - val = 0x8; - break; - case 48000: - val = 0x9; - break; - case 88200: - val = 0xa; - break; - case 96000: - val = 0xb; - break; - case 176400: - val = 0xc; - break; - case 192000: - val = 0xd; - break; - case 384000: - val = 0xe; - break; - case 768000: - val = 0xf; - break; - default: - dev_err(component->dev, "Unsupported sample rate %d\n", - params_rate(params)); - return -EINVAL; - } - - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN, - RT715_SDCA_FREQ_IND_CTRL, CH_00), val); - - return 0; -} - -static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct rt715_sdw_stream_data *stream = - snd_soc_dai_get_dma_data(dai, substream); - - if (!rt715->slave) - return -EINVAL; - - sdw_stream_remove_slave(rt715->slave, stream->sdw_stream); - return 0; -} - -#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) -#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) - -static struct snd_soc_dai_ops rt715_ops = { - .hw_params = rt715_pcm_hw_params, - .hw_free = rt715_pcm_hw_free, - .set_sdw_stream = rt715_set_sdw_stream, - .shutdown = rt715_shutdown, -}; - -static struct snd_soc_dai_driver rt715_dai[] = { - { - .name = "rt715-aif1", - .id = RT715_AIF1, - .capture = { - .stream_name = "DP6 Capture", - .channels_min = 1, - .channels_max = 2, - .rates = RT715_STEREO_RATES, - .formats = RT715_FORMATS, - }, - .ops = &rt715_ops, - }, - { - .name = "rt715-aif2", - .id = RT715_AIF2, - .capture = { - .stream_name = "DP4 Capture", - .channels_min = 1, - .channels_max = 2, - .rates = RT715_STEREO_RATES, - .formats = RT715_FORMATS, - }, - .ops = &rt715_ops, - }, -}; - -/* Bus clock frequency */ -#define RT715_CLK_FREQ_9600000HZ 9600000 -#define RT715_CLK_FREQ_12000000HZ 12000000 -#define RT715_CLK_FREQ_6000000HZ 6000000 -#define RT715_CLK_FREQ_4800000HZ 4800000 -#define RT715_CLK_FREQ_2400000HZ 2400000 -#define RT715_CLK_FREQ_12288000HZ 12288000 - -int rt715_init(struct device *dev, struct regmap *mbq_regmap, - struct regmap *regmap, struct sdw_slave *slave) -{ - struct rt715_sdca_priv *rt715; - int ret; - - rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); - if (!rt715) - return -ENOMEM; - - dev_set_drvdata(dev, rt715); - rt715->slave = slave; - rt715->regmap = regmap; - rt715->mbq_regmap = mbq_regmap; - rt715->hw_sdw_ver = slave->id.sdw_version; - /* - * Mark hw_init to false - * HW init will be performed when device reports present - */ - rt715->hw_init = false; - rt715->first_init = false; - - ret = devm_snd_soc_register_component(dev, - &soc_codec_dev_rt715_sdca, - rt715_dai, - ARRAY_SIZE(rt715_dai)); - - return ret; -} - -int rt715_io_init(struct device *dev, struct sdw_slave *slave) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - unsigned int hw_ver; - - if (rt715->hw_init) - return 0; - - /* - * PM runtime is only enabled when a Slave reports as Attached - */ - if (!rt715->first_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - - /* update count of parent 'active' children */ - pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - - rt715->first_init = true; - } - - pm_runtime_get_noresume(&slave->dev); - - rt715_index_read(rt715, RT715_VENDOR_REG, - RT715_PRODUCT_NUM, &hw_ver); - hw_ver = hw_ver & 0x000f; - - /* set clock selector = external */ - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1); - /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */ - if (hw_ver == 0x0) - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_AD_FUNC_EN, 0x54, 0x54); - else if (hw_ver == 0x1) { - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_AD_FUNC_EN, 0x55, 0x55); - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_REV_1, 0x40, 0x40); - } - /* trigger mode = VAD enable */ - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2); - /* SMPU-1 interrupt enable mask */ - regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1); - - /* Mark Slave initialization complete */ - rt715->hw_init = true; - - pm_runtime_mark_last_busy(&slave->dev); - pm_runtime_put_autosuspend(&slave->dev); - - return 0; -} - -MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA"); -MODULE_AUTHOR("Jack Yu "); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h deleted file mode 100644 index 6326cd8c374e..000000000000 --- a/sound/soc/codecs/rt715-sdca.h +++ /dev/null @@ -1,124 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * rt715-sdca.h -- RT715 ALSA SoC audio driver header - * - * Copyright(c) 2020 Realtek Semiconductor Corp. - */ - -#ifndef __RT715_SDCA_H__ -#define __RT715_SDCA_H__ - -#include -#include -#include -#include -#include -#include - -struct rt715_sdca_priv { - struct regmap *regmap; - struct regmap *mbq_regmap; - struct snd_soc_codec *codec; - struct sdw_slave *slave; - struct delayed_work adc_mute_work; - int dbg_nid; - int dbg_vid; - int dbg_payload; - enum sdw_slave_status status; - struct sdw_bus_params params; - bool hw_init; - bool first_init; - int l_is_unmute; - int r_is_unmute; - int hw_sdw_ver; -}; - -struct rt715_sdw_stream_data { - struct sdw_stream_runtime *sdw_stream; -}; - -/* MIPI Register */ -#define RT715_INT_CTRL 0x005a -#define RT715_INT_MASK 0x005e - -/* NID */ -#define RT715_AUDIO_FUNCTION_GROUP 0x01 -#define RT715_MIC_ADC 0x07 -#define RT715_LINE_ADC 0x08 -#define RT715_MIX_ADC 0x09 -#define RT715_DMIC1 0x12 -#define RT715_DMIC2 0x13 -#define RT715_MIC1 0x18 -#define RT715_MIC2 0x19 -#define RT715_LINE1 0x1a -#define RT715_LINE2 0x1b -#define RT715_DMIC3 0x1d -#define RT715_DMIC4 0x29 -#define RT715_VENDOR_REG 0x20 -#define RT715_MUX_IN1 0x22 -#define RT715_MUX_IN2 0x23 -#define RT715_MUX_IN3 0x24 -#define RT715_MUX_IN4 0x25 -#define RT715_MIX_ADC2 0x27 -#define RT715_INLINE_CMD 0x55 -#define RT715_VENDOR_HDA_CTL 0x61 - -/* Index (NID:20h) */ -#define RT715_PRODUCT_NUM 0x0 -#define RT715_IRQ_CTRL 0x2b -#define RT715_AD_FUNC_EN 0x36 -#define RT715_REV_1 0x37 -#define RT715_SDW_INPUT_SEL 0x39 -#define RT715_EXT_DMIC_CLK_CTRL2 0x54 - -/* Index (NID:61h) */ -#define RT715_HDA_LEGACY_MUX_CTL1 0x00 - -/* SDCA (Function) */ -#define FUN_JACK_CODEC 0x01 -#define FUN_MIC_ARRAY 0x02 -#define FUN_HID 0x03 -/* SDCA (Entity) */ -#define RT715_SDCA_ST_EN 0x00 -#define RT715_SDCA_CS_FREQ_IND_EN 0x01 -#define RT715_SDCA_FU_ADC8_9_VOL 0x02 -#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05 -#define RT715_SDCA_FU_ADC10_11_VOL 0x06 -#define RT715_SDCA_FU_ADC7_27_VOL 0x0a -#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c -#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e -#define RT715_SDCA_CX_CLK_SEL_EN 0x10 -#define RT715_SDCA_CREQ_POW_EN 0x18 -/* SDCA (Control) */ -#define RT715_SDCA_ST_CTRL 0x00 -#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01 -#define RT715_SDCA_REQ_POW_CTRL 0x01 -#define RT715_SDCA_FU_MUTE_CTRL 0x01 -#define RT715_SDCA_FU_VOL_CTRL 0x02 -#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b -#define RT715_SDCA_FREQ_IND_CTRL 0x10 -#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10 -#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11 -/* SDCA (Channel) */ -#define CH_00 0x00 -#define CH_01 0x01 -#define CH_02 0x02 -#define CH_03 0x03 -#define CH_04 0x04 -#define CH_05 0x05 -#define CH_06 0x06 -#define CH_07 0x07 -#define CH_08 0x08 - -#define RT715_SDCA_DB_STEP 375 - -enum { - RT715_AIF1, - RT715_AIF2, -}; - -int rt715_io_init(struct device *dev, struct sdw_slave *slave); -int rt715_init(struct device *dev, struct regmap *mbq_regmap, - struct regmap *regmap, struct sdw_slave *slave); - -#endif /* __RT715_SDCA_H__ */ From 2c2b3ad2c4c801bab1eec7264ea6991b1e4e8f2c Mon Sep 17 00:00:00 2001 From: Maxim Kochetkov Date: Tue, 1 Dec 2020 11:59:16 +0300 Subject: [PATCH 073/127] spi: spi-fsl-dspi: Use max_native_cs instead of num_chipselect to set SPI_MCR If cs-gpios property is used in devicetree then ctlr->num_chipselect value may be changed by spi_get_gpio_descs(). So use ctlr->max_native_cs instead of ctlr->num_chipselect to set SPI_MCR Fixes: 4fcc7c2292de (spi: spi-fsl-dspi: Don't access reserved fields in SPI_MCR) Signed-off-by: Maxim Kochetkov Link: https://lore.kernel.org/r/20201201085916.63543-1-fido_max@inbox.ru Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 1a08c1d584ab..028736687488 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1165,7 +1165,7 @@ static int dspi_init(struct fsl_dspi *dspi) unsigned int mcr; /* Set idle states for all chip select signals to high */ - mcr = SPI_MCR_PCSIS(GENMASK(dspi->ctlr->num_chipselect - 1, 0)); + mcr = SPI_MCR_PCSIS(GENMASK(dspi->ctlr->max_native_cs - 1, 0)); if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) mcr |= SPI_MCR_XSPI; @@ -1250,7 +1250,7 @@ static int dspi_probe(struct platform_device *pdev) pdata = dev_get_platdata(&pdev->dev); if (pdata) { - ctlr->num_chipselect = pdata->cs_num; + ctlr->num_chipselect = ctlr->max_native_cs = pdata->cs_num; ctlr->bus_num = pdata->bus_num; /* Only Coldfire uses platform data */ @@ -1263,7 +1263,7 @@ static int dspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't get spi-num-chipselects\n"); goto out_ctlr_put; } - ctlr->num_chipselect = cs_num; + ctlr->num_chipselect = ctlr->max_native_cs = cs_num; of_property_read_u32(np, "bus-num", &bus_num); ctlr->bus_num = bus_num; From 744ef9b091b8b4f6c6246c8e70dd817175bde8bc Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:05 +0000 Subject: [PATCH 074/127] regulator: Update DA9121 dt-bindings Update bindings for the Dialog Semiconductor DA9121 voltage regulator to add device variants. Because several variants have multiple regulators, and to regard potential to add GPIO support in future, the 'regulators' sub-node is added, following the precedent set by other multi-regulator devices, including the DA9211 family. This breaks compatibility with the original submission by Vincent Whitchurch - but as this is still in for-next, the alignment could be made before upstreaming occurs. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/0606d3ded5fef4c38760246146f197db4ce3a374.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- .../bindings/regulator/dlg,da9121.yaml | 164 ++++++++++++++++-- MAINTAINERS | 2 + .../regulator/dlg,da9121-regulator.h | 22 +++ 3 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 include/dt-bindings/regulator/dlg,da9121-regulator.h diff --git a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml index 2ece46ee1a59..6f2164f7bc57 100644 --- a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml +++ b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml @@ -7,41 +7,183 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Dialog Semiconductor DA9121 voltage regulator maintainers: - - Vincent Whitchurch + - Adam Ward + +description: | + Dialog Semiconductor DA9121 Single-channel 10A double-phase buck converter + Dialog Semiconductor DA9122 Double-channel 5A single-phase buck converter + Dialog Semiconductor DA9220 Double-channel 3A single-phase buck converter + Dialog Semiconductor DA9217 Single-channel 6A double-phase buck converter + Dialog Semiconductor DA9130 Single-channel 10A double-phase buck converter + Dialog Semiconductor DA9131 Double-channel 5A single-phase buck converter + Dialog Semiconductor DA9132 Double-channel 3A single-phase buck converter + + Current limits + + This is PER PHASE, and the current limit setting in the devices reflect + that with a maximum 10A limit. Allowing for transients at/near double + the rated current, this translates across the device range to per + channel figures as so... + + | DA9121 DA9122 DA9220 DA9217 DA9140 + | /DA9130 /DA9131 /DA9132 + ----------------------------------------------------------------------------- + Output current / channel | 10000000 5000000 3000000 6000000 40000000 + Output current / phase | 5000000 5000000 3000000 3000000 9500000 + ----------------------------------------------------------------------------- + Min regulator-min-microvolt| 300000 300000 300000 300000 500000 + Max regulator-max-microvolt| 1900000 1900000 1900000 1900000 1000000 + Device hardware default | 1000000 1000000 1000000 1000000 1000000 + ----------------------------------------------------------------------------- + Min regulator-min-microamp | 7000000 3500000 3500000 7000000 26000000 + Max regulator-max-microamp | 20000000 10000000 6000000 12000000 78000000 + Device hardware default | 15000000 7500000 5500000 11000000 58000000 properties: + $nodename: + pattern: "pmic@[0-9a-f]{1,2}" compatible: - const: dlg,da9121 + enum: + - dlg,da9121 + - dlg,da9122 + - dlg,da9220 + - dlg,da9217 + - dlg,da9130 + - dlg,da9131 + - dlg,da9132 + - dlg,da9140 reg: maxItems: 1 + description: Specifies the I2C slave address. - buck1: - description: - Initial data for the Buck1 regulator. - $ref: "regulator.yaml#" + interrupts: + maxItems: 1 + description: IRQ line information. + + dlg,irq-polling-delay-passive-ms: + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 1000 + maximum: 10000 + description: | + Specify the polling period, measured in milliseconds, between interrupt status + update checks. Range 1000-10000 ms. + + regulators: type: object + $ref: regulator.yaml# + description: | + This node defines the settings for the BUCK. The content of the + sub-node is defined by the standard binding for regulators; see regulator.yaml. + The DA9121 regulator is bound using their names listed below + buck1 - BUCK1 + buck2 - BUCK2 //DA9122, DA9220, DA9131, DA9132 only -additionalProperties: false + patternProperties: + "^buck([1-2])$": + type: object + $ref: regulator.yaml# + + properties: + regulator-mode: + maxItems: 1 + description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h + + regulator-initial-mode: + maxItems: 1 + description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h + + enable-gpios: + maxItems: 1 + description: Specify a valid GPIO for platform control of the regulator + + dlg,ripple-cancel: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: | + Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h + Only present on multi-channel devices (DA9122, DA9220, DA9131, DA9132) + + unevaluatedProperties: false required: - compatible - reg + - regulators + +additionalProperties: false examples: - | + #include + #include + #include i2c { #address-cells = <1>; #size-cells = <0>; - regulator@68 { + pmic@68 { compatible = "dlg,da9121"; reg = <0x68>; - buck1 { - regulator-min-microvolt = <680000>; - regulator-max-microvolt = <820000>; + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + + dlg,irq-polling-delay-passive-ms = <2000>; + + regulators { + DA9121_BUCK1: buck1 { + regulator-name = "BUCK1"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1900000>; + regulator-min-microamp = <7000000>; + regulator-max-microamp = <20000000>; + regulator-boot-on; + regulator-initial-mode = ; + enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + }; }; }; }; + - | + #include + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + pmic@68 { + compatible = "dlg,da9122"; + reg = <0x68>; + + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + + dlg,irq-polling-delay-passive-ms = <2000>; + + regulators { + DA9122_BUCK1: buck1 { + regulator-name = "BUCK1"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1900000>; + regulator-min-microamp = <3500000>; + regulator-max-microamp = <10000000>; + regulator-boot-on; + regulator-initial-mode = ; + enable-gpios = <&gpio6 1 GPIO_ACTIVE_HIGH>; + dlg,ripple-cancel = ; + }; + DA9122_BUCK2: buck2 { + regulator-name = "BUCK2"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1900000>; + regulator-min-microamp = <3500000>; + regulator-max-microamp = <10000000>; + regulator-boot-on; + regulator-initial-mode = ; + enable-gpios = <&gpio6 2 GPIO_ACTIVE_HIGH>; + dlg,ripple-cancel = ; + }; + }; + }; + }; ... diff --git a/MAINTAINERS b/MAINTAINERS index 131860ff307f..1578b0bb6c3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5118,6 +5118,7 @@ S: Supported W: http://www.dialog-semiconductor.com/products F: Documentation/devicetree/bindings/input/da90??-onkey.txt F: Documentation/devicetree/bindings/mfd/da90*.txt +F: Documentation/devicetree/bindings/regulator/dlg,da9*.yaml F: Documentation/devicetree/bindings/regulator/da92*.txt F: Documentation/devicetree/bindings/regulator/slg51000.txt F: Documentation/devicetree/bindings/sound/da[79]*.txt @@ -5142,6 +5143,7 @@ F: drivers/rtc/rtc-da90??.c F: drivers/thermal/da90??-thermal.c F: drivers/video/backlight/da90??_bl.c F: drivers/watchdog/da90??_wdt.c +F: include/dt-bindings/regulator/dlg,da9*-regulator.h F: include/linux/mfd/da903x.h F: include/linux/mfd/da9052/ F: include/linux/mfd/da9055/ diff --git a/include/dt-bindings/regulator/dlg,da9121-regulator.h b/include/dt-bindings/regulator/dlg,da9121-regulator.h new file mode 100644 index 000000000000..954edf633ce7 --- /dev/null +++ b/include/dt-bindings/regulator/dlg,da9121-regulator.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _DT_BINDINGS_REGULATOR_DLG_DA9121_H +#define _DT_BINDINGS_REGULATOR_DLG_DA9121_H + +/* + * These buck mode constants may be used to specify values in device tree + * properties (e.g. regulator-initial-mode). + * A description of the following modes is in the manufacturers datasheet. + */ + +#define DA9121_BUCK_MODE_FORCE_PFM 0 +#define DA9121_BUCK_MODE_FORCE_PWM 1 +#define DA9121_BUCK_MODE_FORCE_PWM_SHEDDING 2 +#define DA9121_BUCK_MODE_AUTO 3 + +#define DA9121_BUCK_RIPPLE_CANCEL_NONE 0 +#define DA9121_BUCK_RIPPLE_CANCEL_SMALL 1 +#define DA9121_BUCK_RIPPLE_CANCEL_MID 2 +#define DA9121_BUCK_RIPPLE_CANCEL_LARGE 3 + +#endif From 86f162c91f274e0d8a0c440d7a991230f6ac7725 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:06 +0000 Subject: [PATCH 075/127] regulator: da9121: Add header file Add header file for Dialog Semiconductor DA9121 regulator and related devices, mostly autogenerated from the chip design databases, and update driver to replace local defines with those from header. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/3527d84448d1e6ddc0fcb883ae564880f75a6cb0.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 15 +- drivers/regulator/da9121-regulator.h | 291 +++++++++++++++++++++++++++ 2 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 drivers/regulator/da9121-regulator.h diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 66bdfd1979c0..c11fe046345f 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -9,12 +9,7 @@ #include #include #include - -#define DA9121_BUCK_BUCK1_0 0x20 -#define DA9121_BUCK_BUCK1_0_CH1_EN BIT(0) - -#define DA9121_BUCK_BUCK1_5 0x25 -#define DA9121_BUCK_BUCK1_5_CH1_A_VOUT GENMASK(7, 0) +#include "da9121-regulator.h" #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 @@ -47,10 +42,10 @@ static const struct regulator_desc da9121_reg = { .min_uV = DA9121_MIN_MV * 1000, .uV_step = DA9121_STEP_MV * 1000, .linear_min_sel = DA9121_MIN_SEL, - .vsel_reg = DA9121_BUCK_BUCK1_5, - .vsel_mask = DA9121_BUCK_BUCK1_5_CH1_A_VOUT, - .enable_reg = DA9121_BUCK_BUCK1_0, - .enable_mask = DA9121_BUCK_BUCK1_0_CH1_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ .ramp_delay = 20000, /* tBUCK_EN */ diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h new file mode 100644 index 000000000000..3c34cb889ca8 --- /dev/null +++ b/drivers/regulator/da9121-regulator.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Steve Twiss, Dialog Semiconductor + * Adam Ward, Dialog Semiconductor + */ + +#ifndef __DA9121_REGISTERS_H__ +#define __DA9121_REGISTERS_H__ + +/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE + * DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL + */ +#include + +enum da9121_variant { + DA9121_TYPE_DA9121_DA9130, + DA9121_TYPE_DA9220_DA9132, + DA9121_TYPE_DA9122_DA9131, + DA9121_TYPE_DA9217 +}; + +/* Minimum, maximum and default polling millisecond periods are provided + * here as an example. It is expected that any final implementation will + * include a modification of these settings to match the required + * application. + */ +#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000 +#define DA9121_MAX_POLLING_PERIOD_MS 10000 +#define DA9121_MIN_POLLING_PERIOD_MS 1000 + +/* Registers */ + +#define DA9121_REG_SYS_STATUS_0 0x01 +#define DA9121_REG_SYS_STATUS_1 0x02 +#define DA9121_REG_SYS_STATUS_2 0x03 +#define DA9121_REG_SYS_EVENT_0 0x04 +#define DA9121_REG_SYS_EVENT_1 0x05 +#define DA9121_REG_SYS_EVENT_2 0x06 +#define DA9121_REG_SYS_MASK_0 0x07 +#define DA9121_REG_SYS_MASK_1 0x08 +#define DA9121_REG_SYS_MASK_2 0x09 +#define DA9121_REG_SYS_MASK_3 0x0A +#define DA9121_REG_SYS_CONFIG_0 0x0B +#define DA9121_REG_SYS_CONFIG_1 0x0C +#define DA9121_REG_SYS_CONFIG_2 0x0D +#define DA9121_REG_SYS_CONFIG_3 0x0E +#define DA9121_REG_SYS_GPIO0_0 0x10 +#define DA9121_REG_SYS_GPIO0_1 0x11 +#define DA9121_REG_SYS_GPIO1_0 0x12 +#define DA9121_REG_SYS_GPIO1_1 0x13 +#define DA9121_REG_SYS_GPIO2_0 0x14 +#define DA9121_REG_SYS_GPIO2_1 0x15 +#define DA9121_REG_BUCK_BUCK1_0 0x20 +#define DA9121_REG_BUCK_BUCK1_1 0x21 +#define DA9121_REG_BUCK_BUCK1_2 0x22 +#define DA9121_REG_BUCK_BUCK1_3 0x23 +#define DA9121_REG_BUCK_BUCK1_4 0x24 +#define DA9121_REG_BUCK_BUCK1_5 0x25 +#define DA9121_REG_BUCK_BUCK1_6 0x26 +#define DA9121_REG_BUCK_BUCK1_7 0x27 +#define DA9xxx_REG_BUCK_BUCK2_0 0x28 +#define DA9xxx_REG_BUCK_BUCK2_1 0x29 +#define DA9xxx_REG_BUCK_BUCK2_2 0x2A +#define DA9xxx_REG_BUCK_BUCK2_3 0x2B +#define DA9xxx_REG_BUCK_BUCK2_4 0x2C +#define DA9xxx_REG_BUCK_BUCK2_5 0x2D +#define DA9xxx_REG_BUCK_BUCK2_6 0x2E +#define DA9xxx_REG_BUCK_BUCK2_7 0x2F +#define DA9121_REG_OTP_DEVICE_ID 0x48 +#define DA9121_REG_OTP_VARIANT_ID 0x49 +#define DA9121_REG_OTP_CUSTOMER_ID 0x4A +#define DA9121_REG_OTP_CONFIG_ID 0x4B + +/* Register bits */ + +/* DA9121_REG_SYS_STATUS_0 */ + +#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2) +#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_STATUS_1 */ + +#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7) +#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6) +#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5) +#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4) +#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3) +#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2) +#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1) +#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0) + +/* DA9121_REG_SYS_STATUS_2 */ + +#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2) +#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1) +#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0) + +/* DA9121_REG_SYS_EVENT_0 */ + +#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_EVENT_1 */ + +#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7) +#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6) +#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5) +#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4) +#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3) +#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2) +#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1) +#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0) + +/* DA9121_REG_SYS_EVENT_2 */ + +#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0) + +/* DA9121_REG_SYS_MASK_0 */ + +#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_MASK_1 */ + +#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7) +#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6) +#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5) +#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4) +#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3) +#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2) +#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1) +#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0) + +/* DA9121_REG_SYS_MASK_2 */ + +#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2) +#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1) +#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0) + +/* DA9122_REG_SYS_MASK_3 */ + +#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3) +#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2) +#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1) +#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0) + +/* DA9121_REG_SYS_CONFIG_0 */ + +#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0 +#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F + +/* DA9xxx_REG_SYS_CONFIG_1 */ + +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0 +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F + +/* DA9121_REG_SYS_CONFIG_2 */ + +#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60 +#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4) +#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C + +/* DA9121_REG_SYS_CONFIG_3 */ + +#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70 +#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1) + +/* DA9121_REG_SYS_GPIO0_0 */ + +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO0_1 */ + +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30 +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO1_0 */ + +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO1_1 */ + +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30 +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO2_0 */ + +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO2_1 */ + +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30 +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03 + +/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */ + +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70 +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E +#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0) + +/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */ + +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70 +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E +#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0) + +/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */ + +#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F + +/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */ + +#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF + +/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */ + +#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4) +#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C +#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03 + +/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */ + +#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */ + +#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */ + +#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03 + + +/* DA9121_REG_OTP_DEVICE_ID */ + +#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF + +#define DA9121_DEVICE_ID 0x05 + +/* DA9121_REG_OTP_VARIANT_ID */ + +#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4 +#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0 +#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0 +#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F + +#define DA9121_VARIANT_MRC_BASE 0x2 +#define DA9121_VARIANT_VRC 0x1 +#define DA9220_VARIANT_VRC 0x0 +#define DA9122_VARIANT_VRC 0x2 +#define DA9217_VARIANT_VRC 0x7 + +/* DA9121_REG_OTP_CUSTOMER_ID */ + +#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF + +/* DA9121_REG_OTP_CONFIG_ID */ + +#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF + +#endif /* __DA9121_REGISTERS_H__ */ From f3fbd5566f6a8cdb7c48ab29bd1096205b7fbcaf Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:07 +0000 Subject: [PATCH 076/127] regulator: da9121: Add device variants Add basic support for configuration to reference variants of this device, and track the selected variant within the driver. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/aaabd3063593e5172fa6b605884d475df64e6d65.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 46 ++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index c11fe046345f..5bebdb2a480d 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -11,6 +11,12 @@ #include #include "da9121-regulator.h" +/* Chip data */ +struct da9121 { + struct device *dev; + int variant_id; +}; + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -53,19 +59,46 @@ static const struct regulator_desc da9121_reg = { }; static const struct of_device_id da9121_dt_ids[] = { - { .compatible = "dlg,da9121", }, + { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9217", .data = (void *) DA9121_TYPE_DA9217 }, + { .compatible = "dlg,da9122", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9131", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9220", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { .compatible = "dlg,da9132", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, { } }; MODULE_DEVICE_TABLE(of, da9121_dt_ids); +static inline int da9121_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da9121_dt_ids, dev); + + if (!id) { + dev_err(dev, "%s: Failed\n", __func__); + return -EINVAL; + } + return (uintptr_t)id->data; +} + static int da9121_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct da9121 *chip; + int ret = 0; struct device *dev = &i2c->dev; struct regulator_config config = {}; struct regulator_dev *rdev; struct regmap *regmap; + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto error; + } + + chip->variant_id = da9121_of_get_id(&i2c->dev); + regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -80,11 +113,18 @@ static int da9121_i2c_probe(struct i2c_client *i2c, return PTR_ERR(rdev); } - return 0; +error: + return ret; } static const struct i2c_device_id da9121_i2c_id[] = { - { "da9121", 0 }, + {"da9121", DA9121_TYPE_DA9121_DA9130}, + {"da9130", DA9121_TYPE_DA9121_DA9130}, + {"da9217", DA9121_TYPE_DA9217}, + {"da9122", DA9121_TYPE_DA9122_DA9131}, + {"da9131", DA9121_TYPE_DA9122_DA9131}, + {"da9220", DA9121_TYPE_DA9220_DA9132}, + {"da9132", DA9121_TYPE_DA9220_DA9132}, {}, }; MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); From c860476b9e3a420192b28e580cb749e024d032eb Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:08 +0000 Subject: [PATCH 077/127] regulator: da9121: Add device variant regmaps Add ability to probe device and validate configuration, then apply a regmap configuration for a single or dual buck device accordingly. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/068c6b8d5e1b4e221e899e4c914c429429a2ec7d.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 14 +- drivers/regulator/da9121-regulator.c | 244 +++++++++++++++++++++++++-- include/linux/regulator/da9121.h | 25 +++ 3 files changed, 262 insertions(+), 21 deletions(-) create mode 100644 include/linux/regulator/da9121.h diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a497a3729091..7faedb05b7a6 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -313,13 +313,21 @@ config REGULATOR_DA9063 will be called da9063-regulator. config REGULATOR_DA9121 - tristate "Dialog Semiconductor DA9121 regulator" + tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator" depends on I2C && OF select REGMAP_I2C help Say y here to support for the Dialog Semiconductor DA9121. The - DA9210 is a dual-phase buck converter controlled through an I2C - interface. + DA9121 is a single channel dual-phase buck converter controlled + through an I2C interface. + + DA9121 Single-channel dual-phase 10A buck converter + DA9130 Single-channel dual-phase 10A buck converter (Automotive) + DA9217 Single-channel dual-phase 6A buck converter + DA9122 Dual-channel single-phase 5A buck converter + DA9131 Dual-channel single-phase 5A buck converter (Automotive) + DA9220 Dual-channel single-phase 3A buck converter + DA9132 Dual-channel single-phase 3A buck converter (Automotive) This driver can also be built as a module. If so, the module will be called da9121-regulator. diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 5bebdb2a480d..137b1dfbcfbb 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -1,5 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (C) 2020 Axis Communications AB */ +// +// DA9121 Single-channel dual-phase 10A buck converter +// +// Copyright (C) 2020 Axis Communications AB +// +// DA9130 Single-channel dual-phase 10A buck converter (Automotive) +// DA9217 Single-channel dual-phase 6A buck converter +// DA9122 Dual-channel single-phase 5A buck converter +// DA9131 Dual-channel single-phase 5A buck converter (Automotive) +// DA9220 Dual-channel single-phase 3A buck converter +// DA9132 Dual-channel single-phase 3A buck converter (Automotive) +// +// Copyright (C) 2020 Dialog Semiconductor #include #include @@ -9,26 +21,17 @@ #include #include #include +#include + #include "da9121-regulator.h" /* Chip data */ struct da9121 { struct device *dev; + struct regmap *regmap; int variant_id; }; -#define DA9121_MIN_MV 300 -#define DA9121_MAX_MV 1900 -#define DA9121_STEP_MV 10 -#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) -#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ - + 1 + DA9121_MIN_SEL) - -static const struct regmap_config da9121_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -38,6 +41,13 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, }; +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + static const struct regulator_desc da9121_reg = { .name = "da9121", .of_match = "buck1", @@ -58,6 +68,205 @@ static const struct regulator_desc da9121_reg = { .enable_time = 20, }; +/* DA9121 chip register model */ +static const struct regmap_range da9121_1ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_1ch_readable_table = { + .yes_ranges = da9121_1ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_readable_ranges), +}; + +static const struct regmap_range da9121_2ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_7), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_2ch_readable_table = { + .yes_ranges = da9121_2ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_readable_ranges), +}; + +static const struct regmap_range da9121_1ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_1ch_writeable_table = { + .yes_ranges = da9121_1ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_writeable_ranges), +}; + +static const struct regmap_range da9121_2ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_2), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_4, DA9xxx_REG_BUCK_BUCK2_7), +}; + +static const struct regmap_access_table da9121_2ch_writeable_table = { + .yes_ranges = da9121_2ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_writeable_ranges), +}; + + +static const struct regmap_range da9121_volatile_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_EVENT_2), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_volatile_table = { + .yes_ranges = da9121_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_volatile_ranges), +}; + +/* DA9121 regmap config for 1 channel variants */ +static struct regmap_config da9121_1ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_1ch_readable_table, + .wr_table = &da9121_1ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +/* DA9121 regmap config for 2 channel variants */ +static struct regmap_config da9121_2ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_2ch_readable_table, + .wr_table = &da9121_2ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) +{ + u32 device_id; + u8 chip_id = chip->variant_id; + u32 variant_id; + u8 variant_mrc, variant_vrc; + char *type; + const char *name; + bool config_match = false; + int ret = 0; + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_DEVICE_ID, &device_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read device ID: %d\n", ret); + goto error; + } + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_VARIANT_ID, &variant_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read variant ID: %d\n", ret); + goto error; + } + + if (device_id != DA9121_DEVICE_ID) { + dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id); + ret = -ENODEV; + goto error; + } + + variant_vrc = variant_id & DA9121_MASK_OTP_VARIANT_ID_VRC; + + switch (variant_vrc) { + case DA9121_VARIANT_VRC: + type = "DA9121/DA9130"; + config_match = (chip_id == DA9121_TYPE_DA9121_DA9130); + break; + case DA9220_VARIANT_VRC: + type = "DA9220/DA9132"; + config_match = (chip_id == DA9121_TYPE_DA9220_DA9132); + break; + case DA9122_VARIANT_VRC: + type = "DA9122/DA9131"; + config_match = (chip_id == DA9121_TYPE_DA9122_DA9131); + break; + case DA9217_VARIANT_VRC: + type = "DA9217"; + config_match = (chip_id == DA9121_TYPE_DA9217); + break; + default: + type = "Unknown"; + break; + } + + dev_info(chip->dev, + "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", + device_id, variant_id, type); + + if (!config_match) { + dev_err(chip->dev, "Device tree configuration '%s' does not match detected device.\n", name); + ret = -EINVAL; + goto error; + } + + variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC) + >> DA9121_SHIFT_OTP_VARIANT_ID_MRC; + + if ((device_id == DA9121_DEVICE_ID) && + (variant_mrc < DA9121_VARIANT_MRC_BASE)) { + dev_err(chip->dev, + "Cannot support variant MRC: 0x%02X\n", variant_mrc); + ret = -EINVAL; + } +error: + return ret; +} + +static int da9121_assign_chip_model(struct i2c_client *i2c, + struct da9121 *chip) +{ + struct regmap_config *regmap; + int ret = 0; + + chip->dev = &i2c->dev; + + switch (chip->variant_id) { + case DA9121_TYPE_DA9121_DA9130: + fallthrough; + case DA9121_TYPE_DA9217: + regmap = &da9121_1ch_regmap_config; + break; + case DA9121_TYPE_DA9122_DA9131: + fallthrough; + case DA9121_TYPE_DA9220_DA9132: + regmap = &da9121_2ch_regmap_config; + break; + } + + chip->regmap = devm_regmap_init_i2c(i2c, regmap); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to configure a register map: %d\n", + ret); + } + + ret = da9121_check_device_type(i2c, chip); + + return ret; +} + static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, @@ -89,7 +298,6 @@ static int da9121_i2c_probe(struct i2c_client *i2c, struct device *dev = &i2c->dev; struct regulator_config config = {}; struct regulator_dev *rdev; - struct regmap *regmap; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); if (!chip) { @@ -99,13 +307,13 @@ static int da9121_i2c_probe(struct i2c_client *i2c, chip->variant_id = da9121_of_get_id(&i2c->dev); - regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + ret = da9121_assign_chip_model(i2c, chip); + if (ret < 0) + goto error; config.dev = &i2c->dev; config.of_node = dev->of_node; - config.regmap = regmap; + config.regmap = chip->regmap; rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); if (IS_ERR(rdev)) { diff --git a/include/linux/regulator/da9121.h b/include/linux/regulator/da9121.h new file mode 100644 index 000000000000..c31180d886cc --- /dev/null +++ b/include/linux/regulator/da9121.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Adam Ward, Dialog Semiconductor + */ + +#ifndef __LINUX_REGULATOR_DA9121_H +#define __LINUX_REGULATOR_DA9121_H + +enum { + DA9121_IDX_BUCK1, + DA9121_IDX_BUCK2, + DA9121_IDX_MAX +}; + +#endif From 91863239ce0366c801f1f128246f30ea80d7727b Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:09 +0000 Subject: [PATCH 078/127] regulator: da9121: Add device variant descriptors Descriptors for bucks in all variants, ready for of_regulator_match Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/8f7a11d59e5ac3ba1bfd448bcfc905efc99b7874.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 137b1dfbcfbb..a717e2b1da33 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -41,6 +41,11 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, }; +static struct of_regulator_match da9121_matches[] = { + [DA9121_IDX_BUCK1] = { .name = "buck1" }, + [DA9121_IDX_BUCK2] = { .name = "buck2" }, +}; + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -49,9 +54,11 @@ static const struct regulator_ops da9121_buck_ops = { + 1 + DA9121_MIN_SEL) static const struct regulator_desc da9121_reg = { + .id = DA9121_IDX_BUCK1, .name = "da9121", .of_match = "buck1", .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -68,6 +75,105 @@ static const struct regulator_desc da9121_reg = { .enable_time = 20, }; +static const struct regulator_desc da9220_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9220/DA9132 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9220/DA9132 BUCK2", + .of_match = "buck2", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9122_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9122/DA9131 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9122/DA9131 BUCK2", + .of_match = "buck2", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9217_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9217 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, +}; + +static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = { + [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL }, + [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] }, + [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] }, + [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, +}; + /* DA9121 chip register model */ static const struct regmap_range da9121_1ch_readable_ranges[] = { regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), @@ -255,6 +361,10 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, break; } + /* Set these up for of_regulator_match call which may want .of_map_modes */ + da9121_matches[0].desc = local_da9121_regulators[chip->variant_id][0]; + da9121_matches[1].desc = local_da9121_regulators[chip->variant_id][1]; + chip->regmap = devm_regmap_init_i2c(i2c, regmap); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); From 46c413d5bb239769e6f1de706adf422c807c7a5f Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:10 +0000 Subject: [PATCH 079/127] regulator: da9121: Add support for device variants via devicetree Add devicetree configuration and device variant parameters. Use the latter to enable the check and use of parameters specific to dual buck variants. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/5849ce60595aef1018bdde7dcfb54a7397597545.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 119 +++++++++++++++++++++++++++ include/linux/regulator/da9121.h | 11 +++ 2 files changed, 130 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index a717e2b1da33..1f74371eebad 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -14,6 +14,7 @@ // Copyright (C) 2020 Dialog Semiconductor #include +#include #include #include #include @@ -28,10 +29,67 @@ /* Chip data */ struct da9121 { struct device *dev; + struct da9121_pdata *pdata; struct regmap *regmap; int variant_id; }; +/* Define ranges for different variants, enabling translation to/from + * registers. Maximums give scope to allow for transients. + */ +struct da9121_range { + int val_min; + int val_max; + int val_stp; + int reg_min; + int reg_max; +}; + +struct da9121_range da9121_10A_2phase_current = { + .val_min = 7000000, + .val_max = 20000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 14, +}; + +struct da9121_range da9121_6A_2phase_current = { + .val_min = 7000000, + .val_max = 12000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_range da9121_5A_1phase_current = { + .val_min = 3500000, + .val_max = 10000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 14, +}; + +struct da9121_range da9121_3A_1phase_current = { + .val_min = 3500000, + .val_max = 6000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_variant_info { + int num_bucks; + int num_phases; + struct da9121_range *current_range; +}; + +static const struct da9121_variant_info variant_parameters[] = { + { 1, 2, &da9121_10A_2phase_current }, //DA9121_TYPE_DA9121_DA9130 + { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132 + { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131 + { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 +}; + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -46,6 +104,59 @@ static struct of_regulator_match da9121_matches[] = { [DA9121_IDX_BUCK2] = { .name = "buck2" }, }; +static int da9121_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct da9121 *chip = config->driver_data; + struct da9121_pdata *pdata; + struct gpio_desc *ena_gpiod; + + if (chip->pdata == NULL) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + pdata = chip->pdata; + } + + pdata->num_buck++; + + if (pdata->num_buck > variant_parameters[chip->variant_id].num_bucks) { + dev_err(chip->dev, "Error: excessive regulators for device\n"); + return -ENODEV; + } + + ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "da9121-enable"); + if (!IS_ERR(ena_gpiod)) + config->ena_gpiod = ena_gpiod; + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + uint32_t ripple_cancel; + uint32_t ripple_reg; + int ret; + + if (of_property_read_u32(da9121_matches[pdata->num_buck].of_node, + "dlg,ripple-cancel", &ripple_cancel)) { + if (pdata->num_buck > 1) + ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; + else + ripple_reg = DA9121_REG_BUCK_BUCK1_7; + + ret = regmap_update_bits(chip->regmap, ripple_reg, + DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL, + ripple_cancel); + if (ret < 0) + dev_err(chip->dev, "Cannot set ripple mode, err: %d\n", ret); + } + } + + return 0; +} + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -57,6 +168,7 @@ static const struct regulator_desc da9121_reg = { .id = DA9121_IDX_BUCK1, .name = "da9121", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -80,6 +192,7 @@ static const struct regulator_desc da9220_reg[2] = { .id = DA9121_IDX_BUCK1, .name = "DA9220/DA9132 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -97,6 +210,7 @@ static const struct regulator_desc da9220_reg[2] = { .id = DA9121_IDX_BUCK2, .name = "DA9220/DA9132 BUCK2", .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -117,6 +231,7 @@ static const struct regulator_desc da9122_reg[2] = { .id = DA9121_IDX_BUCK1, .name = "DA9122/DA9131 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -134,6 +249,7 @@ static const struct regulator_desc da9122_reg[2] = { .id = DA9121_IDX_BUCK2, .name = "DA9122/DA9131 BUCK2", .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -153,6 +269,7 @@ static const struct regulator_desc da9217_reg = { .id = DA9121_IDX_BUCK1, .name = "DA9217 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -415,6 +532,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, goto error; } + chip->pdata = i2c->dev.platform_data; chip->variant_id = da9121_of_get_id(&i2c->dev); ret = da9121_assign_chip_model(i2c, chip); @@ -422,6 +540,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, goto error; config.dev = &i2c->dev; + config.driver_data = chip; config.of_node = dev->of_node; config.regmap = chip->regmap; diff --git a/include/linux/regulator/da9121.h b/include/linux/regulator/da9121.h index c31180d886cc..62d9d257dc25 100644 --- a/include/linux/regulator/da9121.h +++ b/include/linux/regulator/da9121.h @@ -16,10 +16,21 @@ #ifndef __LINUX_REGULATOR_DA9121_H #define __LINUX_REGULATOR_DA9121_H +#include + +struct gpio_desc; + enum { DA9121_IDX_BUCK1, DA9121_IDX_BUCK2, DA9121_IDX_MAX }; +struct da9121_pdata { + int num_buck; + struct gpio_desc *gpiod_ren[DA9121_IDX_MAX]; + struct device_node *reg_node[DA9121_IDX_MAX]; + struct regulator_init_data *init_data[DA9121_IDX_MAX]; +}; + #endif From 9929900d1878565a90a70bf25baa7d3e4187ae99 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:11 +0000 Subject: [PATCH 080/127] regulator: da9121: Update registration to support multiple buck variants Add function which iterates the regulator descriptors for the confirmed variant ID and registers each buck. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/1fd53c3ab032ef3c8a92f80a2247381db1c09ced.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 44 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 1f74371eebad..6c82441dda57 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -31,6 +31,7 @@ struct da9121 { struct device *dev; struct da9121_pdata *pdata; struct regmap *regmap; + struct regulator_dev *rdev[DA9121_IDX_MAX]; int variant_id; }; @@ -291,6 +292,35 @@ static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, }; +static int da9121_set_regulator_config(struct da9121 *chip) +{ + struct regulator_config config = { }; + unsigned int max_matches = variant_parameters[chip->variant_id].num_bucks; + int ret = 0; + int i; + + for (i = 0; i < max_matches; i++) { + const struct regulator_desc *regl_desc = + local_da9121_regulators[chip->variant_id][i]; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + chip->rdev[i] = devm_regulator_register(chip->dev, + regl_desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, "Failed to register regulator %s, %d/%d\n", + regl_desc->name, (i+1), max_matches); + ret = PTR_ERR(chip->rdev[i]); + goto error; + } + } + +error: + return ret; +} + /* DA9121 chip register model */ static const struct regmap_range da9121_1ch_readable_ranges[] = { regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), @@ -522,9 +552,6 @@ static int da9121_i2c_probe(struct i2c_client *i2c, { struct da9121 *chip; int ret = 0; - struct device *dev = &i2c->dev; - struct regulator_config config = {}; - struct regulator_dev *rdev; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); if (!chip) { @@ -539,16 +566,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; - config.dev = &i2c->dev; - config.driver_data = chip; - config.of_node = dev->of_node; - config.regmap = chip->regmap; - - rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); - if (IS_ERR(rdev)) { - dev_err(&i2c->dev, "Failed to register da9121 regulator\n"); - return PTR_ERR(rdev); - } + ret = da9121_set_regulator_config(chip); error: return ret; From 5c4b62af1d58fe3823dfb462ce553ceb9729c8fa Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:12 +0000 Subject: [PATCH 081/127] regulator: da9121: add current support This commit adds support for getting/setting current for all supported variants. Limits are adjusted per variant to match HW implementation. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/9aa80b909893dbe609730919ed595c6a8ac26606.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 113 +++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 6c82441dda57..a69acb26f748 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -91,6 +91,117 @@ static const struct da9121_variant_info variant_parameters[] = { { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 }; +struct da9121_field { + unsigned int reg; + unsigned int msk; +}; + +static const struct da9121_field da9121_current_field[2] = { + { DA9121_REG_BUCK_BUCK1_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, + { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, +}; + +static int da9121_get_current_limit(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_current_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + goto error; + } + + if (val < range->reg_min) { + ret = -EACCES; + goto error; + } + + if (val > range->reg_max) { + ret = -EINVAL; + goto error; + } + + return range->val_min + (range->val_stp * (val - range->reg_min)); +error: + return ret; +} + +static int da9121_ceiling_selector(struct regulator_dev *rdev, + int min, int max, + unsigned int *selector) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int level; + unsigned int i = 0; + unsigned int sel = 0; + int ret = 0; + + if (range->val_min > max || range->val_max < min) { + dev_err(chip->dev, + "Requested current out of regulator capability\n"); + ret = -EINVAL; + goto error; + } + + level = range->val_max; + for (i = range->reg_max; i >= range->reg_min; i--) { + if (level <= max) { + sel = i; + break; + } + level -= range->val_stp; + } + + if (level < min) { + dev_err(chip->dev, + "Best match falls below minimum requested current\n"); + ret = -EINVAL; + goto error; + } + + *selector = sel; +error: + return ret; +} + +static int da9121_set_current_limit(struct regulator_dev *rdev, + int min_ua, int max_ua) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int sel = 0; + int ret = 0; + + if (min_ua < range->val_min || + max_ua > range->val_max) { + ret = -EINVAL; + goto error; + } + + ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel); + if (ret < 0) + goto error; + + ret = regmap_update_bits(chip->regmap, + da9121_current_field[id].reg, + da9121_current_field[id].msk, + (unsigned int)sel); + if (ret < 0) + dev_err(chip->dev, "Cannot update BUCK current limit, err: %d\n", ret); + +error: + return ret; +} + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -98,6 +209,8 @@ static const struct regulator_ops da9121_buck_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, + .get_current_limit = da9121_get_current_limit, + .set_current_limit = da9121_set_current_limit, }; static struct of_regulator_match da9121_matches[] = { From 65ac97042d4e0918b401b5f21e2810efa27be848 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:13 +0000 Subject: [PATCH 082/127] regulator: da9121: add mode support Adds get/set for mode, and mapping from REGULATOR_MODE_* to select PFM/PWM/Auto operation. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/7844c8f6facb6f7c0649381629cc75ccad14723d.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index a69acb26f748..8e50f55af45d 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -101,6 +101,11 @@ static const struct da9121_field da9121_current_field[2] = { { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, }; +static const struct da9121_field da9121_mode_field[2] = { + { DA9121_REG_BUCK_BUCK1_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, + { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, +}; + static int da9121_get_current_limit(struct regulator_dev *rdev) { struct da9121 *chip = rdev_get_drvdata(rdev); @@ -202,6 +207,67 @@ error: return ret; } +static unsigned int da9121_map_mode(unsigned int mode) +{ + switch (mode) { + case DA9121_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + case DA9121_BUCK_MODE_FORCE_PWM_SHEDDING: + return REGULATOR_MODE_NORMAL; + case DA9121_BUCK_MODE_AUTO: + return REGULATOR_MODE_IDLE; + case DA9121_BUCK_MODE_FORCE_PFM: + return REGULATOR_MODE_STANDBY; + default: + return -EINVAL; + } +} + +static int da9121_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9121_BUCK_MODE_FORCE_PWM; + break; + case REGULATOR_MODE_NORMAL: + val = DA9121_BUCK_MODE_FORCE_PWM_SHEDDING; + break; + case REGULATOR_MODE_IDLE: + val = DA9121_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = DA9121_BUCK_MODE_FORCE_PFM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(chip->regmap, + da9121_mode_field[id].reg, + da9121_mode_field[id].msk, + val); +} + +static unsigned int da9121_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_mode_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + return -EINVAL; + } + + return da9121_map_mode(val & da9121_mode_field[id].msk); +} + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -211,6 +277,8 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, .get_current_limit = da9121_get_current_limit, .set_current_limit = da9121_set_current_limit, + .set_mode = da9121_buck_set_mode, + .get_mode = da9121_buck_get_mode, }; static struct of_regulator_match da9121_matches[] = { @@ -285,6 +353,7 @@ static const struct regulator_desc da9121_reg = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -309,6 +378,7 @@ static const struct regulator_desc da9220_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -327,6 +397,7 @@ static const struct regulator_desc da9220_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -348,6 +419,7 @@ static const struct regulator_desc da9122_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -366,6 +438,7 @@ static const struct regulator_desc da9122_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -386,6 +459,7 @@ static const struct regulator_desc da9217_reg = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, From 40bb5b02ff10f6b68925850e7dedaeec532abb88 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:14 +0000 Subject: [PATCH 083/127] regulator: da9121: add interrupt support Adds interrupt handler for variants, and notifications for events; over temperature/voltage/current. Because the IRQs are triggered by persisting status, they must be masked and the status polled until clear, before the IRQ can be enabled again. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/fe21796bbcbadff84a472a4cc581ae8fafc7f8f5.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 286 +++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 8e50f55af45d..d9a8a4b8fb3b 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -23,15 +23,21 @@ #include #include #include +#include +#include #include "da9121-regulator.h" /* Chip data */ struct da9121 { struct device *dev; + struct delayed_work work; struct da9121_pdata *pdata; struct regmap *regmap; struct regulator_dev *rdev[DA9121_IDX_MAX]; + unsigned int persistent[2]; + unsigned int passive_delay; + int chip_irq; int variant_id; }; @@ -106,6 +112,59 @@ static const struct da9121_field da9121_mode_field[2] = { { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, }; +struct status_event_data { + int buck_id; /* 0=core, 1/2-buck */ + int reg_index; /* index for status/event/mask register selection */ + int status_bit; /* bit masks... */ + int event_bit; + int mask_bit; + unsigned long notification; /* Notification for status inception */ + char *warn; /* if NULL, notify - otherwise dev_warn this string */ +}; + +#define DA9121_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9121_MASK_SYS_STATUS_##bank##_##name, \ + DA9121_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9121_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* For second buck related event bits that are specific to DA9122, DA9220 variants */ +#define DA9xxx_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9xxx_MASK_SYS_STATUS_##bank##_##name, \ + DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* The status signals that may need servicing, depending on device variant. + * After assertion, they persist; so event is notified, the IRQ disabled, + * and status polled until clear again and IRQ is reenabled. + * + * SG/PG1/PG2 should be set when device first powers up and should never + * re-occur. When this driver starts, it is expected that these will have + * self-cleared for when the IRQs are enabled, so these should never be seen. + * If seen, the implication is that the device has reset. + * + * GPIO0/1/2 are not configured for use by default, so should not be seen. + */ +const struct status_event_data status_event_handling[] = { + DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), + DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), + DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), + DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"), + DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"), + DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"), + DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"), + DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"), +}; + static int da9121_get_current_limit(struct regulator_dev *rdev) { struct da9121 *chip = rdev_get_drvdata(rdev); @@ -479,6 +538,157 @@ static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, }; +static void da9121_status_poll_on(struct work_struct *work) +{ + struct da9121 *chip = container_of(work, struct da9121, work.work); + int status[3] = {0}; + int clear[3] = {0}; + unsigned long delay; + int i; + int ret; + + ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read STATUS registers: %d\n", ret); + goto error; + } + + /* Possible events are tested to be within range for the variant, potentially + * masked by the IRQ handler (not just warned about), as having been masked, + * and the respective state cleared - then flagged to unmask for next IRQ. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool supported = (item->warn == NULL); + bool persisting = (chip->persistent[reg_idx] & item->event_bit); + bool now_cleared = !(status[reg_idx] & item->status_bit); + + if (relevant && supported && persisting && now_cleared) { + clear[reg_idx] |= item->mask_bit; + chip->persistent[reg_idx] &= ~item->event_bit; + } + } + + for (i = 0; i < 2; i++) { + if (clear[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = clear[i]; + + ret = regmap_update_bits(chip->regmap, reg, mbit, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to unmask 0x%02x %d\n", + reg, ret); + goto error; + } + } + } + + if (chip->persistent[0] | chip->persistent[1]) { + delay = msecs_to_jiffies(chip->passive_delay); + queue_delayed_work(system_freezable_wq, &chip->work, delay); + } + +error: + return; +} + +static irqreturn_t da9121_irq_handler(int irq, void *data) +{ + struct da9121 *chip = data; + struct regulator_dev *rdev; + int event[3] = {0}; + int handled[3] = {0}; + int mask[3] = {0}; + int ret = IRQ_NONE; + int i; + int err; + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3); + if (err < 0) { + dev_err(chip->dev, "Failed to read EVENT registers %d\n", err); + ret = IRQ_NONE; + goto error; + } + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, mask, 3); + if (err < 0) { + dev_err(chip->dev, + "Failed to read MASK registers: %d\n", ret); + ret = IRQ_NONE; + goto error; + } + + rdev = chip->rdev[DA9121_IDX_BUCK1]; + + /* Possible events are tested to be within range for the variant, currently + * enabled, and having triggered this IRQ. The event may then be notified, + * or a warning given for unexpected events - those from device POR, and + * currently unsupported GPIO configurations. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool enabled = !(mask[reg_idx] & item->mask_bit); + bool active = (event[reg_idx] & item->event_bit); + bool notify = (item->warn == NULL); + + if (relevant && enabled && active) { + if (notify) { + chip->persistent[reg_idx] |= item->event_bit; + regulator_notifier_call_chain(rdev, item->notification, NULL); + } else { + dev_warn(chip->dev, item->warn); + handled[reg_idx] |= item->event_bit; + ret = IRQ_HANDLED; + } + } + } + + for (i = 0; i < 3; i++) { + if (event[i] != handled[i]) { + dev_warn(chip->dev, + "Unhandled event(s) in bank%d 0x%02x\n", i, + event[i] ^ handled[i]); + } + } + + /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */ + for (i = 0; i < 2; i++) { + if (handled[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = handled[i]; + + err = regmap_update_bits(chip->regmap, reg, mbit, mbit); + if (err < 0) { + dev_err(chip->dev, + "Failed to mask 0x%02x interrupt %d\n", + reg, err); + ret = IRQ_NONE; + goto error; + } + } + } + + /* clear the events */ + if (handled[0] | handled[1] | handled[2]) { + err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3); + if (err < 0) { + dev_err(chip->dev, "Fail to write EVENTs %d\n", err); + ret = IRQ_NONE; + goto error; + } + } + + queue_delayed_work(system_freezable_wq, &chip->work, 0); +error: + return ret; +} + static int da9121_set_regulator_config(struct da9121 *chip) { struct regulator_config config = { }; @@ -711,6 +921,56 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, return ret; } +static int da9121_config_irq(struct i2c_client *i2c, + struct da9121 *chip) +{ + unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + const int mask_all[4] = { 0, 0, 0xFF, 0xFF }; + int ret = 0; + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + if (!of_property_read_u32(chip->dev->of_node, + "dlg,irq-polling-delay-passive-ms", + &p_delay)) { + if (p_delay < DA9121_MIN_POLLING_PERIOD_MS || + p_delay > DA9121_MAX_POLLING_PERIOD_MS) { + dev_warn(chip->dev, + "Out-of-range polling period %d ms\n", + p_delay); + p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + } + } + + chip->passive_delay = p_delay; + + ret = devm_request_threaded_irq(chip->dev, + chip->chip_irq, NULL, + da9121_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9121", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed IRQ request: %d\n", + chip->chip_irq); + goto error; + } + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", + ret); + goto error; + } + + INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); + dev_info(chip->dev, "Interrupt polling period set at %d ms\n", + chip->passive_delay); + } +error: + return ret; +} + static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, @@ -738,6 +998,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da9121 *chip; + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; int ret = 0; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); @@ -753,12 +1014,36 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + ret = da9121_set_regulator_config(chip); + if (ret < 0) + goto error; + + ret = da9121_config_irq(i2c, chip); error: return ret; } +static int da9121_i2c_remove(struct i2c_client *i2c) +{ + struct da9121 *chip = i2c_get_clientdata(i2c); + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + cancel_delayed_work_sync(&chip->work); + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + return ret; +} + static const struct i2c_device_id da9121_i2c_id[] = { {"da9121", DA9121_TYPE_DA9121_DA9130}, {"da9130", DA9121_TYPE_DA9121_DA9130}, @@ -777,6 +1062,7 @@ static struct i2c_driver da9121_regulator_driver = { .of_match_table = of_match_ptr(da9121_dt_ids), }, .probe = da9121_i2c_probe, + .remove = da9121_i2c_remove, .id_table = da9121_i2c_id, }; From 5e191d2e05a4fe098632006bb3afa5e21c8789db Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Wed, 2 Dec 2020 11:32:47 +0000 Subject: [PATCH 084/127] regulator: da9121: Request IRQ directly and free in release function to avoid masking race Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/24b2d8b0c3536408369add034221b1141e58bbfc.1606908582.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index d9a8a4b8fb3b..3ead6a171d95 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -945,8 +945,7 @@ static int da9121_config_irq(struct i2c_client *i2c, chip->passive_delay = p_delay; - ret = devm_request_threaded_irq(chip->dev, - chip->chip_irq, NULL, + ret = request_threaded_irq(chip->chip_irq, NULL, da9121_irq_handler, IRQF_TRIGGER_LOW|IRQF_ONESHOT, "da9121", chip); @@ -960,7 +959,7 @@ static int da9121_config_irq(struct i2c_client *i2c, if (ret != 0) { dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); - goto error; + goto regmap_error; } INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); @@ -969,6 +968,9 @@ static int da9121_config_irq(struct i2c_client *i2c, } error: return ret; +regmap_error: + free_irq(chip->chip_irq, chip); + return ret; } static const struct of_device_id da9121_dt_ids[] = { @@ -1036,6 +1038,7 @@ static int da9121_i2c_remove(struct i2c_client *i2c) const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; int ret = 0; + free_irq(chip->chip_irq, chip); cancel_delayed_work_sync(&chip->work); ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); From c8dbf2f200de8c9cba3b332522fabad114cf9f53 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 3 Dec 2020 19:26:35 +0800 Subject: [PATCH 085/127] regulator: da9121: Mark some symbols with static keyword Fix the following sparse warnings: drivers/regulator/da9121-regulator.c:55:21: warning: symbol 'da9121_10A_2phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:63:21: warning: symbol 'da9121_6A_2phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:71:21: warning: symbol 'da9121_5A_1phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:79:21: warning: symbol 'da9121_3A_1phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:151:32: warning: symbol 'status_event_handling' was not declared. Should it be static? Signed-off-by: Zou Wei Acked-by: Adam Ward Link: https://lore.kernel.org/r/1606994795-36182-1-git-send-email-zou_wei@huawei.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 3ead6a171d95..893512cee9e4 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -52,7 +52,7 @@ struct da9121_range { int reg_max; }; -struct da9121_range da9121_10A_2phase_current = { +static struct da9121_range da9121_10A_2phase_current = { .val_min = 7000000, .val_max = 20000000, .val_stp = 1000000, @@ -60,7 +60,7 @@ struct da9121_range da9121_10A_2phase_current = { .reg_max = 14, }; -struct da9121_range da9121_6A_2phase_current = { +static struct da9121_range da9121_6A_2phase_current = { .val_min = 7000000, .val_max = 12000000, .val_stp = 1000000, @@ -68,7 +68,7 @@ struct da9121_range da9121_6A_2phase_current = { .reg_max = 6, }; -struct da9121_range da9121_5A_1phase_current = { +static struct da9121_range da9121_5A_1phase_current = { .val_min = 3500000, .val_max = 10000000, .val_stp = 500000, @@ -76,7 +76,7 @@ struct da9121_range da9121_5A_1phase_current = { .reg_max = 14, }; -struct da9121_range da9121_3A_1phase_current = { +static struct da9121_range da9121_3A_1phase_current = { .val_min = 3500000, .val_max = 6000000, .val_stp = 500000, @@ -148,7 +148,7 @@ struct status_event_data { * * GPIO0/1/2 are not configured for use by default, so should not be seen. */ -const struct status_event_data status_event_handling[] = { +static const struct status_event_data status_event_handling[] = { DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), From b4b277760a2167ddb28a309b81363889efd5cc22 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 4 Dec 2020 17:52:22 +0100 Subject: [PATCH 086/127] regulator: da9121: include linux/gpio/consumer.h When CONFIG_GPIOLIB is disabled, the declarations from linux/gpio/consumer.h are not visible: drivers/regulator/da9121-regulator.c:371:14: error: implicit declaration of function 'fwnode_gpiod_get_index' [-Werror,-Wimplicit-function-declaration] ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, ^ drivers/regulator/da9121-regulator.c:372:7: error: use of undeclared identifier 'GPIOD_OUT_HIGH' GPIOD_OUT_HIGH | ^ drivers/regulator/da9121-regulator.c:373:7: error: use of undeclared identifier 'GPIOD_FLAGS_BIT_NONEXCLUSIVE' GPIOD_FLAGS_BIT_NONEXCLUSIVE, Include this explicitly to help compile testing. Fixes: 46c413d5bb23 ("regulator: da9121: Add support for device variants via devicetree") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20201204165229.3754763-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 893512cee9e4..db1c2cc838bc 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include From ff7f380d21d0e530c3501a007cec68da6dd4d650 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 3 Dec 2020 12:42:43 +0530 Subject: [PATCH 087/127] regulator: dt-bindings: Add PM8350x compatibles Add PM8350 and PM8350C compatibles for these PMICs found in some Qualcomm platforms. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20201203071244.2652297-1-vkoul@kernel.org Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/qcom,rpmh-regulator.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt index bae558b87686..b8f0b7809c02 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt @@ -26,6 +26,8 @@ Supported regulator node names: PM8009: smps1 - smps2, ldo1 - ldo7 PM8150: smps1 - smps10, ldo1 - ldo18 PM8150L: smps1 - smps8, ldo1 - ldo11, bob, flash, rgb + PM8350: smps1 - smps12, ldo1 - ldo10, + PM8350C: smps1 - smps10, ldo1 - ldo13, bob PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2 PMI8998: bob PM6150: smps1 - smps5, ldo1 - ldo19 @@ -44,6 +46,8 @@ First Level Nodes - PMIC "qcom,pm8009-rpmh-regulators" "qcom,pm8150-rpmh-regulators" "qcom,pm8150l-rpmh-regulators" + "qcom,pm8350-rpmh-regulators" + "qcom,pm8350c-rpmh-regulators" "qcom,pm8998-rpmh-regulators" "qcom,pmi8998-rpmh-regulators" "qcom,pm6150-rpmh-regulators" From bebb2c6d5ca23d6b7556d39564212b619e068562 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 3 Dec 2020 12:42:44 +0530 Subject: [PATCH 088/127] regulator: qcom-rpmh: Add support for PM8350/PM8350c Add support from RPMH regulators found in PM8350 and PM8350c PMICs Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20201203071244.2652297-2-vkoul@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index e673d48b31a1..fe030ec4b7db 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {}, +}; + static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), @@ -1011,6 +1065,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8150l-rpmh-regulators", .data = pm8150l_vreg_data, }, + { + .compatible = "qcom,pm8350-rpmh-regulators", + .data = pm8350_vreg_data, + }, + { + .compatible = "qcom,pm8350c-rpmh-regulators", + .data = pm8350c_vreg_data, + }, { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, From b8450e014214982a6df3e62a5bee6c37b94f6b98 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 4 Dec 2020 10:24:09 +0200 Subject: [PATCH 089/127] spi: pxa2xx: Add support for Intel Alder Lake PCH-S Add support for LPSS SPI on Intel Alder Lake. It has four LPSS SPI controllers each having two chip selects. Signed-off-by: Jarkko Nikula Link: https://lore.kernel.org/r/20201204082409.183700-1-jarkko.nikula@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 814268405ab0..62a0f0f86553 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1496,6 +1496,11 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac6), LPSS_BXT_SSP }, + /* ADL-S */ + { PCI_VDEVICE(INTEL, 0x7aaa), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7af9), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7afb), LPSS_CNL_SSP }, /* CNL-LP */ { PCI_VDEVICE(INTEL, 0x9daa), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x9dab), LPSS_CNL_SSP }, From 291de1d102fafef0798cdad9666cd4f8da7da7cc Mon Sep 17 00:00:00 2001 From: DingHua Ma Date: Tue, 1 Dec 2020 08:10:00 +0800 Subject: [PATCH 090/127] regulator: axp20x: Fix DLDO2 voltage control register mask for AXP22x When I use the axp20x chip to power my SDIO device on the 5.4 kernel, the output voltage of DLDO2 is wrong. After comparing the register manual and source code of the chip, I found that the mask bit of the driver register of the port was wrong. I fixed this error by modifying the mask register of the source code. This error seems to be a copy error of the macro when writing the code. Now the voltage output of the DLDO2 port of axp20x is correct. My development environment is Allwinner A40I of arm architecture, and the kernel version is 5.4. Signed-off-by: DingHua Ma Reviewed-by: Chen-Yu Tsai Cc: Fixes: db4a555f7c4c ("regulator: axp20x: use defines for masks") Link: https://lore.kernel.org/r/20201201001000.22302-1-dinghua.ma.sz@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index cd1224182ad7..90cb8445f721 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -594,7 +594,7 @@ static const struct regulator_desc axp22x_regulators[] = { AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, From e748edd9841306908b4e02dddd0afd1aa1f8b973 Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 4 Dec 2020 16:42:37 +0800 Subject: [PATCH 091/127] spi: dw: Fix error return code in dw_spi_bt1_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: abf00907538e ("spi: dw: Add Baikal-T1 SPI Controller glue driver") Reported-by: Hulk Robot Signed-off-by: Zhang Changzhong Acked-by: Serge Semin Link: https://lore.kernel.org/r/1607071357-33378-1-git-send-email-zhangchangzhong@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-bt1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index f382dfad7842..c279b7891e3a 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -280,8 +280,10 @@ static int dw_spi_bt1_probe(struct platform_device *pdev) dws->bus_num = pdev->id; dws->reg_io_width = 4; dws->max_freq = clk_get_rate(dwsbt1->clk); - if (!dws->max_freq) + if (!dws->max_freq) { + ret = -EINVAL; goto err_disable_clk; + } init_func = device_get_match_data(&pdev->dev); ret = init_func(pdev, dwsbt1); From cac8c821059639b015586abf61623c62cc549a13 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 7 Dec 2020 15:59:56 +0200 Subject: [PATCH 092/127] spi: atmel-quadspi: Fix AHB memory accesses Following error was seen when mounting a 16MByte ubifs: UBIFS error (ubi0:0 pid 1893): check_lpt_type.constprop.6: invalid type (15) in LPT node type QSPI_IFR.TFRTYP was not set correctly. When data transfer is enabled and one wants to access the serial memory through AHB in order to: - read in the serial memory, but not a memory data, for example a JEDEC-ID, QSPI_IFR.TFRTYP must be written to '0' (both sama5d2 and sam9x60). - read in the serial memory, and particularly a memory data, TFRTYP must be written to '1' (both sama5d2 and sam9x60). - write in the serial memory, but not a memory data, for example writing the configuration or the QSPI_SR, TFRTYP must be written to '2' for sama5d2 and to '0' for sam9x60. - write in the serial memory in particular to program a memory data, TFRTYP must be written to '3' for sama5d2 and to '1' for sam9x60. Fix the setting of the QSPI_IFR.TFRTYP field. Fixes: 2d30ac5ed633 ("mtd: spi-nor: atmel-quadspi: Use spi-mem interface for atmel-quadspi driver") Cc: # v5.0+ Reported-by: Tom Burkart Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201207135959.154124-2-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 8c009c175f2c..67ef98822366 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -365,10 +365,14 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, if (dummy_cycles) ifr |= QSPI_IFR_NBDUM(dummy_cycles); - /* Set data enable */ - if (op->data.nbytes) + /* Set data enable and data transfer type. */ + if (op->data.nbytes) { ifr |= QSPI_IFR_DATAEN; + if (op->addr.nbytes) + ifr |= QSPI_IFR_TFRTYP_MEM; + } + /* * If the QSPI controller is set in regular SPI mode, set it in * Serial Memory Mode (SMM). @@ -393,7 +397,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, atmel_qspi_write(icr, aq, QSPI_WICR); atmel_qspi_write(ifr, aq, QSPI_IFR); } else { - if (op->data.dir == SPI_MEM_DATA_OUT) + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; /* Set QSPI Instruction Frame registers */ From a6ff3a784ff9975dc77676827a2f448203511d19 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 7 Dec 2020 15:59:57 +0200 Subject: [PATCH 093/127] spi: atmel-quadspi: Drop superfluous set of QSPI_IFR_APBTFRTYP_READ That bit describes the APB transfer type. We are writing serial memory registers via AHB acesses, that bit does not make sense in the current context. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201207135959.154124-3-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 67ef98822366..7fb302e4063d 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -386,9 +386,6 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, (void)atmel_qspi_read(aq, QSPI_SR); if (aq->caps->has_ricr) { - if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN) - ifr |= QSPI_IFR_APBTFRTYP_READ; - /* Set QSPI Instruction Frame registers */ atmel_qspi_write(iar, aq, QSPI_IAR); if (op->data.dir == SPI_MEM_DATA_IN) From d00364b6a60475cd75fd07e847ad6f955952638b Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 7 Dec 2020 15:59:58 +0200 Subject: [PATCH 094/127] spi: atmel-quadspi: Write QSPI_IAR only when needed The address must be written in QSPI_IAR only when we have a instruction frame with address but no data. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201207135959.154124-4-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 7fb302e4063d..3a978ee8b70f 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -385,9 +385,11 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, /* Clear pending interrupts */ (void)atmel_qspi_read(aq, QSPI_SR); - if (aq->caps->has_ricr) { - /* Set QSPI Instruction Frame registers */ + /* Set QSPI Instruction Frame registers. */ + if (op->addr.nbytes && !op->data.nbytes) atmel_qspi_write(iar, aq, QSPI_IAR); + + if (aq->caps->has_ricr) { if (op->data.dir == SPI_MEM_DATA_IN) atmel_qspi_write(icr, aq, QSPI_RICR); else @@ -397,8 +399,6 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; - /* Set QSPI Instruction Frame registers */ - atmel_qspi_write(iar, aq, QSPI_IAR); atmel_qspi_write(icr, aq, QSPI_ICR); atmel_qspi_write(ifr, aq, QSPI_IFR); } From c066efb07d1e8b801ea9d0727119958c9904e63d Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 7 Dec 2020 15:59:59 +0200 Subject: [PATCH 095/127] spi: atmel-quadspi: Move common code outside of if else QSPI_IFR is set as the last QSPI Instruction Frame register regardless of the sama5d2 or sam9x60 version of the IP. Move the writing of QSPI_IFR outside of the IP specific code. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201207135959.154124-5-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 3a978ee8b70f..f56640d63982 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -394,15 +394,15 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, atmel_qspi_write(icr, aq, QSPI_RICR); else atmel_qspi_write(icr, aq, QSPI_WICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } else { if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; atmel_qspi_write(icr, aq, QSPI_ICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } + atmel_qspi_write(ifr, aq, QSPI_IFR); + return 0; } From 373afef350a93519b4b8d636b0895da8650b714b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:01 +0100 Subject: [PATCH 096/127] spi: davinci: Fix use-after-free on unbind davinci_spi_remove() accesses the driver's private data after it's been freed with spi_master_put(). Fix by moving the spi_master_put() to the end of the function. Fixes: fe5fd2540947 ("spi: davinci: Use dma_request_chan() for requesting DMA channel") Signed-off-by: Lukas Wunner Acked-by: Peter Ujfalusi Cc: # v4.7+ Link: https://lore.kernel.org/r/412f7eb1cf8990e0a3a2153f4c577298deab623e.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 818f2b22875d..7453a1dbbc06 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -1040,13 +1040,13 @@ static int davinci_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&dspi->bitbang); clk_disable_unprepare(dspi->clk); - spi_master_put(master); if (dspi->dma_rx) { dma_release_channel(dspi->dma_rx); dma_release_channel(dspi->dma_tx); } + spi_master_put(master); return 0; } From 8f96c434dfbc85ffa755d6634c8c1cb2233fcf24 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:02 +0100 Subject: [PATCH 097/127] spi: spi-geni-qcom: Fix use-after-free on unbind spi_geni_remove() accesses the driver's private data after calling spi_unregister_master() even though that function releases the last reference on the spi_master and thereby frees the private data. Moreover, since commit 1a9e489e6128 ("spi: spi-geni-qcom: Use OPP API to set clk/perf state"), spi_geni_probe() leaks the spi_master allocation if the calls to dev_pm_opp_set_clkname() or dev_pm_opp_of_add_table() fail. Fix by switching over to the new devm_spi_alloc_master() helper which keeps the private data accessible until the driver has unbound and also avoids the spi_master leak on probe. Fixes: 561de45f72bd ("spi: spi-geni-qcom: Add SPI driver support for GENI based QUP") Signed-off-by: Lukas Wunner Cc: # v4.20+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v4.20+ Cc: Rajendra Nayak Cc: Girish Mahadevan Link: https://lore.kernel.org/r/dfa1d8c41b8acdfad87ec8654cd124e6e3cb3f31.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 25810a7eef10..0e3d8e6c08f4 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -603,7 +603,7 @@ static int spi_geni_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - spi = spi_alloc_master(dev, sizeof(*mas)); + spi = devm_spi_alloc_master(dev, sizeof(*mas)); if (!spi) return -ENOMEM; @@ -673,7 +673,6 @@ spi_geni_probe_free_irq: free_irq(mas->irq, spi); spi_geni_probe_runtime_disable: pm_runtime_disable(dev); - spi_master_put(spi); dev_pm_opp_of_remove_table(&pdev->dev); put_clkname: dev_pm_opp_put_clkname(mas->se.opp_table); From 6cfd39e212dee2e77a0227ce4e0f55fa06d79f46 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:03 +0100 Subject: [PATCH 098/127] spi: spi-qcom-qspi: Fix use-after-free on unbind qcom_qspi_remove() accesses the driver's private data after calling spi_unregister_master() even though that function releases the last reference on the spi_master and thereby frees the private data. Fix by switching over to the new devm_spi_alloc_master() helper which keeps the private data accessible until the driver has unbound. Fixes: f79a158d37c2 ("spi: spi-qcom-qspi: Use OPP API to set clk/perf state") Signed-off-by: Lukas Wunner Cc: # v5.9+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.9+ Cc: Rajendra Nayak Link: https://lore.kernel.org/r/b6d3c4dce571d78a532fd74f27def0d5dc8d8a24.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 42 ++++++++++++++----------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 5eed88af6899..8863be370884 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -462,7 +462,7 @@ static int qcom_qspi_probe(struct platform_device *pdev) dev = &pdev->dev; - master = spi_alloc_master(dev, sizeof(*ctrl)); + master = devm_spi_alloc_master(dev, sizeof(*ctrl)); if (!master) return -ENOMEM; @@ -473,54 +473,49 @@ static int qcom_qspi_probe(struct platform_device *pdev) spin_lock_init(&ctrl->lock); ctrl->dev = dev; ctrl->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ctrl->base)) { - ret = PTR_ERR(ctrl->base); - goto exit_probe_master_put; - } + if (IS_ERR(ctrl->base)) + return PTR_ERR(ctrl->base); ctrl->clks = devm_kcalloc(dev, QSPI_NUM_CLKS, sizeof(*ctrl->clks), GFP_KERNEL); - if (!ctrl->clks) { - ret = -ENOMEM; - goto exit_probe_master_put; - } + if (!ctrl->clks) + return -ENOMEM; ctrl->clks[QSPI_CLK_CORE].id = "core"; ctrl->clks[QSPI_CLK_IFACE].id = "iface"; ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks); if (ret) - goto exit_probe_master_put; + return ret; ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config"); - if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) { - ret = dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), - "Failed to get cpu path\n"); - goto exit_probe_master_put; - } + if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) + return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), + "Failed to get cpu path\n"); + /* Set BW vote for register access */ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), Bps_to_icc(1000)); if (ret) { dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", __func__, ret); - goto exit_probe_master_put; + return ret; } ret = icc_disable(ctrl->icc_path_cpu_to_qspi); if (ret) { dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", __func__, ret); - goto exit_probe_master_put; + return ret; } ret = platform_get_irq(pdev, 0); if (ret < 0) - goto exit_probe_master_put; + return ret; ret = devm_request_irq(dev, ret, qcom_qspi_irq, IRQF_TRIGGER_HIGH, dev_name(dev), ctrl); if (ret) { dev_err(dev, "Failed to request irq %d\n", ret); - goto exit_probe_master_put; + return ret; } master->max_speed_hz = 300000000; @@ -537,10 +532,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); - if (IS_ERR(ctrl->opp_table)) { - ret = PTR_ERR(ctrl->opp_table); - goto exit_probe_master_put; - } + if (IS_ERR(ctrl->opp_table)) + return PTR_ERR(ctrl->opp_table); /* OPP table is optional */ ret = dev_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) { @@ -562,9 +555,6 @@ static int qcom_qspi_probe(struct platform_device *pdev) exit_probe_put_clkname: dev_pm_opp_put_clkname(ctrl->opp_table); -exit_probe_master_put: - spi_master_put(master); - return ret; } From e77df3eca12be4b17f13cf9f215cff248c57d98f Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:04 +0100 Subject: [PATCH 099/127] spi: spi-sh: Fix use-after-free on unbind spi_sh_remove() accesses the driver's private data after calling spi_unregister_master() even though that function releases the last reference on the spi_master and thereby frees the private data. Fix by switching over to the new devm_spi_alloc_master() helper which keeps the private data accessible until the driver has unbound. Fixes: 680c1305e259 ("spi/spi_sh: use spi_unregister_master instead of spi_master_put in remove path") Signed-off-by: Lukas Wunner Cc: # v3.0+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v3.0+ Cc: Axel Lin Link: https://lore.kernel.org/r/6d97628b536baf01d5e3e39db61108f84d44c8b2.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-sh.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 20bdae5fdf3b..15123a8f41e1 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -440,7 +440,7 @@ static int spi_sh_probe(struct platform_device *pdev) if (irq < 0) return irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data)); if (master == NULL) { dev_err(&pdev->dev, "spi_alloc_master error.\n"); return -ENOMEM; @@ -458,16 +458,14 @@ static int spi_sh_probe(struct platform_device *pdev) break; default: dev_err(&pdev->dev, "No support width\n"); - ret = -ENODEV; - goto error1; + return -ENODEV; } ss->irq = irq; ss->master = master; ss->addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (ss->addr == NULL) { dev_err(&pdev->dev, "ioremap error.\n"); - ret = -ENOMEM; - goto error1; + return -ENOMEM; } INIT_LIST_HEAD(&ss->queue); spin_lock_init(&ss->lock); @@ -477,7 +475,7 @@ static int spi_sh_probe(struct platform_device *pdev) ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss); if (ret < 0) { dev_err(&pdev->dev, "request_irq error\n"); - goto error1; + return ret; } master->num_chipselect = 2; @@ -496,9 +494,6 @@ static int spi_sh_probe(struct platform_device *pdev) error3: free_irq(irq, ss); - error1: - spi_master_put(master); - return ret; } From 5626308bb94d9f930aa5f7c77327df4c6daa7759 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:05 +0100 Subject: [PATCH 100/127] spi: pxa2xx: Fix use-after-free on unbind pxa2xx_spi_remove() accesses the driver's private data after calling spi_unregister_controller() even though that function releases the last reference on the spi_controller and thereby frees the private data. Fix by switching over to the new devm_spi_alloc_master/slave() helper which keeps the private data accessible until the driver has unbound. Fixes: 32e5b57232c0 ("spi: pxa2xx: Fix controller unregister order") Signed-off-by: Lukas Wunner Cc: # v2.6.17+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v2.6.17+: 32e5b57232c0: spi: pxa2xx: Fix controller unregister order Cc: # v2.6.17+ Link: https://lore.kernel.org/r/5764b04d4a6e43069ebb7808f64c2f774ac6f193.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 62a0f0f86553..bd2354fd438d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1691,9 +1691,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } if (platform_info->is_slave) - controller = spi_alloc_slave(dev, sizeof(struct driver_data)); + controller = devm_spi_alloc_slave(dev, sizeof(*drv_data)); else - controller = spi_alloc_master(dev, sizeof(struct driver_data)); + controller = devm_spi_alloc_master(dev, sizeof(*drv_data)); if (!controller) { dev_err(&pdev->dev, "cannot alloc spi_controller\n"); @@ -1916,7 +1916,6 @@ out_error_dma_irq_alloc: free_irq(ssp->irq, drv_data); out_error_controller_alloc: - spi_controller_put(controller); pxa_ssp_free(ssp); return status; } From 393f981ca5f797b58b882d42b7621fb6e43c7f5b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:06 +0100 Subject: [PATCH 101/127] spi: rpc-if: Fix use-after-free on unbind rpcif_spi_remove() accesses the driver's private data after calling spi_unregister_controller() even though that function releases the last reference on the spi_controller and thereby frees the private data. Fix by switching over to the new devm_spi_alloc_master() helper which keeps the private data accessible until the driver has unbound. Fixes: eb8d6d464a27 ("spi: add Renesas RPC-IF driver") Signed-off-by: Lukas Wunner Cc: # v5.9+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.9+ Cc: Sergei Shtylyov Link: https://lore.kernel.org/r/c5da472c28021da2f6517441685cef033d40b140.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-rpc-if.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index ed3e548227f4..3579675485a5 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -134,7 +134,7 @@ static int rpcif_spi_probe(struct platform_device *pdev) struct rpcif *rpc; int error; - ctlr = spi_alloc_master(&pdev->dev, sizeof(*rpc)); + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*rpc)); if (!ctlr) return -ENOMEM; @@ -159,13 +159,8 @@ static int rpcif_spi_probe(struct platform_device *pdev) error = spi_register_controller(ctlr); if (error) { dev_err(&pdev->dev, "spi_register_controller failed\n"); - goto err_put_ctlr; + rpcif_disable_rpm(rpc); } - return 0; - -err_put_ctlr: - rpcif_disable_rpm(rpc); - spi_controller_put(ctlr); return error; } From cc53711b2191cf3b3210283ae89bf0abb98c70a3 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:07 +0100 Subject: [PATCH 102/127] spi: mxic: Don't leak SPI master in probe error path If the calls to devm_clk_get() or devm_ioremap_resource() fail on probe of the Macronix SPI driver, the spi_master struct is erroneously not freed. Fix by switching over to the new devm_spi_alloc_master() helper. Fixes: b942d80b0a39 ("spi: Add MXIC controller driver") Signed-off-by: Lukas Wunner Cc: # v5.0+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.0+ Cc: Mason Yang Link: https://lore.kernel.org/r/4fa6857806e7e75741c05d057ac9df3564460114.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-mxic.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 8c630acb0110..96b418293bf2 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -529,7 +529,7 @@ static int mxic_spi_probe(struct platform_device *pdev) struct mxic_spi *mxic; int ret; - master = spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi)); if (!master) return -ENOMEM; @@ -574,15 +574,9 @@ static int mxic_spi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret) { dev_err(&pdev->dev, "spi_register_master failed\n"); - goto err_put_master; + pm_runtime_disable(&pdev->dev); } - return 0; - -err_put_master: - spi_master_put(master); - pm_runtime_disable(&pdev->dev); - return ret; } From 0f4ad8d59f33b24dd86739f3be23e6af1a86f5a9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:08 +0100 Subject: [PATCH 103/127] spi: spi-mtk-nor: Don't leak SPI master in probe error path If the call to devm_spi_register_controller() fails on probe of the MediaTek SPI NOR driver, the spi_controller struct is erroneously not freed. Since commit a1daaa991ed1 ("spi: spi-mtk-nor: use dma_alloc_coherent() for bounce buffer"), the same happens if the call to dmam_alloc_coherent() fails. Since commit 3bfd9103c7af ("spi: spi-mtk-nor: Add power management support"), the same happens if the call to mtk_nor_enable_clk() fails. Fix by switching over to the new devm_spi_alloc_master() helper. Fixes: 881d1ee9fe81 ("spi: add support for mediatek spi-nor controller") Signed-off-by: Lukas Wunner Reviewed-by: Ikjoon Jang Cc: # v5.7+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.7+ Cc: Chuanhong Guo Link: https://lore.kernel.org/r/d5b9f0289465394e73dedb8ec51e180a8f1dffc9.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index bf2d0f9cdd19..2e2f36a2e385 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -781,7 +781,7 @@ static int mtk_nor_probe(struct platform_device *pdev) return -EINVAL; } - ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp)); if (!ctlr) { dev_err(&pdev->dev, "failed to allocate spi controller\n"); return -ENOMEM; From 7174dc655ef0578877b0b4598e69619d2be28b4d Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:09 +0100 Subject: [PATCH 104/127] spi: gpio: Don't leak SPI master in probe error path If the call to devm_spi_register_master() fails on probe of the GPIO SPI driver, the spi_master struct is erroneously not freed: After allocating the spi_master, its reference count is 1. The driver unconditionally decrements the reference count on unbind using a devm action. Before calling devm_spi_register_master(), the driver unconditionally increments the reference count because on success, that function will decrement the reference count on unbind. However on failure, devm_spi_register_master() does *not* decrement the reference count, so the spi_master is leaked. The issue was introduced by commits 8b797490b4db ("spi: gpio: Make sure spi_master_put() is called in every error path") and 79567c1a321e ("spi: gpio: Use devm_spi_register_master()"), which sought to plug leaks introduced by 9b00bc7b901f ("spi: spi-gpio: Rewrite to use GPIO descriptors") but missed this remaining leak. The situation was later aggravated by commit d3b0ffa1d75d ("spi: gpio: prevent memory leak in spi_gpio_probe"), which introduced a use-after-free because it releases a reference on the spi_master if devm_add_action_or_reset() fails even though the function already does that. Fix by switching over to the new devm_spi_alloc_master() helper. Fixes: 9b00bc7b901f ("spi: spi-gpio: Rewrite to use GPIO descriptors") Signed-off-by: Lukas Wunner Reviewed-by: Linus Walleij Cc: # v4.17+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.1-: 8b797490b4db: spi: gpio: Make sure spi_master_put() is called in every error path Cc: # v5.1-: 45beec351998: spi: bitbang: Introduce spi_bitbang_init() Cc: # v5.1-: 79567c1a321e: spi: gpio: Use devm_spi_register_master() Cc: # v5.4-: d3b0ffa1d75d: spi: gpio: prevent memory leak in spi_gpio_probe Cc: # v4.17+ Cc: Navid Emamdoost Cc: Andrey Smirnov Link: https://lore.kernel.org/r/86eaed27431c3d709e3748eb76ceecbfc790dd37.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 7ceb0ba27b75..0584f4d2fde2 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -350,11 +350,6 @@ static int spi_gpio_probe_pdata(struct platform_device *pdev, return 0; } -static void spi_gpio_put(void *data) -{ - spi_master_put(data); -} - static int spi_gpio_probe(struct platform_device *pdev) { int status; @@ -363,16 +358,10 @@ static int spi_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct spi_bitbang *bb; - master = spi_alloc_master(dev, sizeof(*spi_gpio)); + master = devm_spi_alloc_master(dev, sizeof(*spi_gpio)); if (!master) return -ENOMEM; - status = devm_add_action_or_reset(&pdev->dev, spi_gpio_put, master); - if (status) { - spi_master_put(master); - return status; - } - if (pdev->dev.of_node) status = spi_gpio_probe_dt(pdev, master); else @@ -432,7 +421,7 @@ static int spi_gpio_probe(struct platform_device *pdev) if (status) return status; - return devm_spi_register_master(&pdev->dev, spi_master_get(master)); + return devm_spi_register_master(&pdev->dev, master); } MODULE_ALIAS("platform:" DRIVER_NAME); From a4729c3506c3eb1a6ca5c0289f4e7cafa4115065 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:10 +0100 Subject: [PATCH 105/127] spi: rb4xx: Don't leak SPI master in probe error path If the calls to devm_clk_get(), devm_spi_register_master() or clk_prepare_enable() fail on probe of the Mikrotik RB4xx SPI driver, the spi_master struct is erroneously not freed. Fix by switching over to the new devm_spi_alloc_master() helper. Fixes: 05aec357871f ("spi: Add SPI driver for Mikrotik RB4xx series boards") Signed-off-by: Lukas Wunner Cc: # v4.2+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v4.2+ Cc: Bert Vermeulen Link: https://lore.kernel.org/r/369bf26d71927f60943b1d9d8f51810f00b0237d.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-rb4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 8aa51beb4ff3..9f97d18a05c1 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -143,7 +143,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev) if (IS_ERR(spi_base)) return PTR_ERR(spi_base); - master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(*rbspi)); if (!master) return -ENOMEM; From 5b8c88462d83331dacb48aeaec8388117fef82e0 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:11 +0100 Subject: [PATCH 106/127] spi: sc18is602: Don't leak SPI master in probe error path If the call to devm_gpiod_get_optional() fails on probe of the NXP SC18IS602/603 SPI driver, the spi_master struct is erroneously not freed. Fix by switching over to the new devm_spi_alloc_master() helper. Fixes: f99008013e19 ("spi: sc18is602: Add reset control via gpio pin.") Signed-off-by: Lukas Wunner Cc: # v4.9+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v4.9+ Cc: Phil Reid Link: https://lore.kernel.org/r/d5f715527b894b91d530fe11a86f51b3184a4e1a.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index ee0f3edf49cd..297c512069a5 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -238,13 +238,12 @@ static int sc18is602_probe(struct i2c_client *client, struct sc18is602_platform_data *pdata = dev_get_platdata(dev); struct sc18is602 *hw; struct spi_master *master; - int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return -EINVAL; - master = spi_alloc_master(dev, sizeof(struct sc18is602)); + master = devm_spi_alloc_master(dev, sizeof(struct sc18is602)); if (!master) return -ENOMEM; @@ -298,15 +297,7 @@ static int sc18is602_probe(struct i2c_client *client, master->min_speed_hz = hw->freq / 128; master->max_speed_hz = hw->freq / 4; - error = devm_spi_register_master(dev, master); - if (error) - goto error_reg; - - return 0; - -error_reg: - spi_master_put(master); - return error; + return devm_spi_register_master(dev, master); } static const struct i2c_device_id sc18is602_id[] = { From e297ddf296de35037fa97f4302782def196d350a Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:12 +0100 Subject: [PATCH 107/127] media: netup_unidvb: Don't leak SPI master in probe error path If the call to spi_register_master() fails on probe of the NetUP Universal DVB driver, the spi_master struct is erroneously not freed. Likewise, if spi_new_device() fails, the spi_controller struct is not unregistered. Plug the leaks. While at it, fix an ordering issue in netup_spi_release() wherein spi_unregister_master() is called after fiddling with the IRQ control register. The correct order is to call spi_unregister_master() *before* this teardown step because bus accesses may still be ongoing until that function returns. Fixes: 52b1eaf4c59a ("[media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver") Signed-off-by: Lukas Wunner Reviewed-by: Mauro Carvalho Chehab Cc: # v4.3+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v4.3+ Cc: Kozlov Sergey Link: https://lore.kernel.org/r/c4c24f333fc7840f4a3db24789e6e10dd660bede.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/media/pci/netup_unidvb/netup_unidvb_spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c index d4f12c250f91..526042d8afae 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_spi.c @@ -175,7 +175,7 @@ int netup_spi_init(struct netup_unidvb_dev *ndev) struct spi_master *master; struct netup_spi *nspi; - master = spi_alloc_master(&ndev->pci_dev->dev, + master = devm_spi_alloc_master(&ndev->pci_dev->dev, sizeof(struct netup_spi)); if (!master) { dev_err(&ndev->pci_dev->dev, @@ -208,6 +208,7 @@ int netup_spi_init(struct netup_unidvb_dev *ndev) ndev->pci_slot, ndev->pci_func); if (!spi_new_device(master, &netup_spi_board)) { + spi_unregister_master(master); ndev->spi = NULL; dev_err(&ndev->pci_dev->dev, "%s(): unable to create SPI device\n", __func__); @@ -226,13 +227,13 @@ void netup_spi_release(struct netup_unidvb_dev *ndev) if (!spi) return; + spi_unregister_master(spi->master); spin_lock_irqsave(&spi->lock, flags); reg = readw(&spi->regs->control_stat); writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat); reg = readw(&spi->regs->control_stat); writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat); spin_unlock_irqrestore(&spi->lock, flags); - spi_unregister_master(spi->master); ndev->spi = NULL; } From 24f7033405abe195224ec793dbc3d7a27dec0b98 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:13 +0100 Subject: [PATCH 108/127] spi: mt7621: Disable clock in probe error path Commit 702b15cb9712 ("spi: mt7621: fix missing clk_disable_unprepare() on error in mt7621_spi_probe") sought to disable the SYS clock on probe errors, but only did so for 2 of 3 potentially failing calls: The clock needs to be disabled on failure of devm_spi_register_controller() as well. Moreover, the commit purports to fix a bug in commit cbd66c626e16 ("spi: mt7621: Move SPI driver out of staging") but in reality the bug has existed since the driver was first introduced. Fixes: 1ab7f2a43558 ("staging: mt7621-spi: add mt7621 support") Signed-off-by: Lukas Wunner Cc: # v4.17+: 702b15cb9712: spi: mt7621: fix missing clk_disable_unprepare() on error in mt7621_spi_probe Cc: # v4.17+ Cc: Qinglang Miao Link: https://lore.kernel.org/r/36ad42760087952fb7c10aae7d2628547c26a7ec.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index 2cdae7994e2a..e227700808cb 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -382,7 +382,11 @@ static int mt7621_spi_probe(struct platform_device *pdev) return ret; } - return devm_spi_register_controller(&pdev->dev, master); + ret = devm_spi_register_controller(&pdev->dev, master); + if (ret) + clk_disable_unprepare(clk); + + return ret; } static int mt7621_spi_remove(struct platform_device *pdev) From 46b5c4fb87ce8211e0f9b0383dbde72c3652d2ba Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:14 +0100 Subject: [PATCH 109/127] spi: mt7621: Don't leak SPI master in probe error path If the calls to device_reset() or devm_spi_register_controller() fail on probe of the MediaTek MT7621 SPI driver, the spi_controller struct is erroneously not freed. Fix by switching over to the new devm_spi_alloc_master() helper. Additionally, there's an ordering issue in mt7621_spi_remove() wherein the spi_controller is unregistered after disabling the SYS clock. The correct order is to call spi_unregister_controller() *before* this teardown step because bus accesses may still be ongoing until that function returns. All of these bugs have existed since the driver was first introduced, so it seems fair to fix them together in a single commit. Fixes: 1ab7f2a43558 ("staging: mt7621-spi: add mt7621 support") Signed-off-by: Lukas Wunner Reviewed-by: Stefan Roese Cc: # v4.17+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v4.17+ Link: https://lore.kernel.org/r/72b680796149f5fcda0b3f530ffb7ee73b04f224.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index e227700808cb..b4b9b7309b5e 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -350,7 +350,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) if (status) return status; - master = spi_alloc_master(&pdev->dev, sizeof(*rs)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(*rs)); if (!master) { dev_info(&pdev->dev, "master allocation failed\n"); clk_disable_unprepare(clk); @@ -382,7 +382,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) return ret; } - ret = devm_spi_register_controller(&pdev->dev, master); + ret = spi_register_controller(master); if (ret) clk_disable_unprepare(clk); @@ -397,6 +397,7 @@ static int mt7621_spi_remove(struct platform_device *pdev) master = dev_get_drvdata(&pdev->dev); rs = spi_controller_get_devdata(master); + spi_unregister_controller(master); clk_disable_unprepare(rs->clk); return 0; From 236924ee531d6251c8d10e9177b7742a60534ed5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:15 +0100 Subject: [PATCH 110/127] spi: ar934x: Don't leak SPI master in probe error path If the call to devm_spi_register_controller() fails on probe of the Qualcomm Atheros AR934x/QCA95xx SPI driver, the spi_controller struct is erroneously not freed. Fix by switching over to the new devm_spi_alloc_master() helper. Moreover, the controller's clock is enabled on probe but not disabled if any of the subsequent probe steps fail. Finally, there's an ordering issue in ar934x_spi_remove() wherein the clock is disabled even though the controller is not yet unregistered. It is unregistered after ar934x_spi_remove() by the devres framework. As long as it is not unregistered, SPI transfers may still be ongoing and disabling the clock may break them. It is not possible to use devm_spi_register_controller() in this case, so move to the non-devm variant. All of these bugs have existed since the driver was first introduced, so it seems fair to fix them together in a single commit. Fixes: 047980c582af ("spi: add driver for ar934x spi controller") Signed-off-by: Lukas Wunner Cc: # v5.7+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.7+ Cc: Chuanhong Guo Link: https://lore.kernel.org/r/1d58367d74d55741e0c2730a51a2b65012c8ab33.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-ar934x.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-ar934x.c b/drivers/spi/spi-ar934x.c index d08dec09d423..def32e0aaefe 100644 --- a/drivers/spi/spi-ar934x.c +++ b/drivers/spi/spi-ar934x.c @@ -176,10 +176,11 @@ static int ar934x_spi_probe(struct platform_device *pdev) if (ret) return ret; - ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp)); if (!ctlr) { dev_info(&pdev->dev, "failed to allocate spi controller\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_clk_disable; } /* disable flash mapping and expose spi controller registers */ @@ -202,7 +203,13 @@ static int ar934x_spi_probe(struct platform_device *pdev) sp->clk_freq = clk_get_rate(clk); sp->ctlr = ctlr; - return devm_spi_register_controller(&pdev->dev, ctlr); + ret = spi_register_controller(ctlr); + if (!ret) + return 0; + +err_clk_disable: + clk_disable_unprepare(clk); + return ret; } static int ar934x_spi_remove(struct platform_device *pdev) @@ -213,6 +220,7 @@ static int ar934x_spi_remove(struct platform_device *pdev) ctlr = dev_get_drvdata(&pdev->dev); sp = spi_controller_get_devdata(ctlr); + spi_unregister_controller(ctlr); clk_disable_unprepare(sp->clk); return 0; From 234266a5168bbe8220d263e3aa7aa80cf921c483 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:16 +0100 Subject: [PATCH 111/127] spi: npcm-fiu: Disable clock in probe error path If the call to devm_spi_register_master() fails on probe of the NPCM FIU SPI driver, the clock "fiu->clk" is erroneously not unprepared and disabled. Fix it. Fixes: ace55c411b11 ("spi: npcm-fiu: add NPCM FIU controller driver") Signed-off-by: Lukas Wunner Cc: # v5.4+ Cc: Tomer Maimon Link: https://lore.kernel.org/r/9ae62f4e1cfe542bec57ac2743e6fca9f9548f55.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-fiu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 1cb9329de945..b62471ab6d7f 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -677,7 +677,7 @@ static int npcm_fiu_probe(struct platform_device *pdev) struct npcm_fiu_spi *fiu; void __iomem *regbase; struct resource *res; - int id; + int id, ret; ctrl = devm_spi_alloc_master(dev, sizeof(*fiu)); if (!ctrl) @@ -735,7 +735,11 @@ static int npcm_fiu_probe(struct platform_device *pdev) ctrl->num_chipselect = fiu->info->max_cs; ctrl->dev.of_node = dev->of_node; - return devm_spi_register_master(dev, ctrl); + ret = devm_spi_register_master(dev, ctrl); + if (ret) + clk_disable_unprepare(fiu->clk); + + return ret; } static int npcm_fiu_remove(struct platform_device *pdev) From c7b884561cb5b641f3dbba950094110794119a6d Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 7 Dec 2020 09:17:17 +0100 Subject: [PATCH 112/127] spi: atmel-quadspi: Fix use-after-free on unbind atmel_qspi_remove() accesses the driver's private data after calling spi_unregister_controller() even though that function releases the last reference on the spi_controller and thereby frees the private data. Fix by switching over to the new devm_spi_alloc_master() helper which keeps the private data accessible until the driver has unbound. Fixes: 2d30ac5ed633 ("mtd: spi-nor: atmel-quadspi: Use spi-mem interface for atmel-quadspi driver") Signed-off-by: Lukas Wunner Cc: # v5.0+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation Cc: # v5.0+ Cc: Piotr Bugalski Link: https://lore.kernel.org/r/4b05c65cf6f1ea3251484fe9a00b4c65478a1ae3.1607286887.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index b44521d4a245..1742ccc1ad0e 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -535,7 +535,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) struct resource *res; int irq, err = 0; - ctrl = spi_alloc_master(&pdev->dev, sizeof(*aq)); + ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*aq)); if (!ctrl) return -ENOMEM; @@ -557,8 +557,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) aq->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(aq->regs)) { dev_err(&pdev->dev, "missing registers\n"); - err = PTR_ERR(aq->regs); - goto exit; + return PTR_ERR(aq->regs); } /* Map the AHB memory */ @@ -566,8 +565,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) aq->mem = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(aq->mem)) { dev_err(&pdev->dev, "missing AHB memory\n"); - err = PTR_ERR(aq->mem); - goto exit; + return PTR_ERR(aq->mem); } aq->mmap_size = resource_size(res); @@ -579,15 +577,14 @@ static int atmel_qspi_probe(struct platform_device *pdev) if (IS_ERR(aq->pclk)) { dev_err(&pdev->dev, "missing peripheral clock\n"); - err = PTR_ERR(aq->pclk); - goto exit; + return PTR_ERR(aq->pclk); } /* Enable the peripheral clock */ err = clk_prepare_enable(aq->pclk); if (err) { dev_err(&pdev->dev, "failed to enable the peripheral clock\n"); - goto exit; + return err; } aq->caps = of_device_get_match_data(&pdev->dev); @@ -638,8 +635,6 @@ disable_qspick: clk_disable_unprepare(aq->qspick); disable_pclk: clk_disable_unprepare(aq->pclk); -exit: - spi_controller_put(ctrl); return err; } From 416c29e9ce1347ba9a4ef7aeb4f30c8d9a3ada49 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 7 Dec 2020 17:15:15 +0000 Subject: [PATCH 113/127] regulator: da9121: Remove uninitialised string variable Erroneously left in when switched to using of_parse_cb() Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/c7a9e947a9582fe0150d860b5eab7e093cd832bb.1607361013.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index db1c2cc838bc..ed68259bd952 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -816,7 +816,6 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) u32 variant_id; u8 variant_mrc, variant_vrc; char *type; - const char *name; bool config_match = false; int ret = 0; @@ -867,7 +866,7 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) device_id, variant_id, type); if (!config_match) { - dev_err(chip->dev, "Device tree configuration '%s' does not match detected device.\n", name); + dev_err(chip->dev, "Device tree configuration does not match detected device.\n"); ret = -EINVAL; goto error; } From 9536ce63705952be5214544e3b048c78f932e794 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 7 Dec 2020 17:15:16 +0000 Subject: [PATCH 114/127] regulator: da9121: Fix index used for DT property Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/2cf324b68d37c4059c7995e8cab5fc9a608ea65d.1607361013.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index ed68259bd952..9d5b02fb1657 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -381,7 +381,7 @@ static int da9121_of_parse_cb(struct device_node *np, uint32_t ripple_reg; int ret; - if (of_property_read_u32(da9121_matches[pdata->num_buck].of_node, + if (of_property_read_u32(da9121_matches[pdata->num_buck-1].of_node, "dlg,ripple-cancel", &ripple_cancel)) { if (pdata->num_buck > 1) ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; From 8db06423e079b1f6c0657e5bebda0006acf75c3c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 7 Dec 2020 20:55:44 +0300 Subject: [PATCH 115/127] regulator: da9121: Potential Oops in da9121_assign_chip_model() There is a missing "return ret;" on this error path so we call "da9121_check_device_type(i2c, chip);" which will end up dereferencing "chip->regmap" and lead to an Oops. Fixes: c860476b9e3a ("regulator: da9121: Add device variant regmaps") Signed-off-by: Dan Carpenter Acked-by: Adam Ward Link: https://lore.kernel.org/r/X85soGKnWAjPUA7a@mwanda Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index db1c2cc838bc..e4fc3a7cd5d8 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -915,6 +915,7 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, ret = PTR_ERR(chip->regmap); dev_err(chip->dev, "Failed to configure a register map: %d\n", ret); + return ret; } ret = da9121_check_device_type(i2c, chip); From 76347344c522da78be29403dda81463ffae2bc99 Mon Sep 17 00:00:00 2001 From: Yash Shah Date: Tue, 8 Dec 2020 10:25:34 +0530 Subject: [PATCH 116/127] spi: Update DT binding docs to support SiFive FU740 SoC Add new compatible strings to the DT binding documents to support SiFive FU740-C000. Signed-off-by: Yash Shah Link: https://lore.kernel.org/r/1607403341-57214-3-git-send-email-yash.shah@sifive.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-sifive.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-sifive.yaml b/Documentation/devicetree/bindings/spi/spi-sifive.yaml index 56dcf1d35da4..6e7e394fc1e4 100644 --- a/Documentation/devicetree/bindings/spi/spi-sifive.yaml +++ b/Documentation/devicetree/bindings/spi/spi-sifive.yaml @@ -17,15 +17,17 @@ allOf: properties: compatible: items: - - const: sifive,fu540-c000-spi + - enum: + - sifive,fu540-c000-spi + - sifive,fu740-c000-spi - const: sifive,spi0 description: Should be "sifive,-spi" and "sifive,spi". Supported compatible strings are - - "sifive,fu540-c000-spi" for the SiFive SPI v0 as integrated - onto the SiFive FU540 chip, and "sifive,spi0" for the SiFive - SPI v0 IP block with no chip integration tweaks. + "sifive,fu540-c000-spi" and "sifive,fu740-c000-spi" for the SiFive SPI v0 + as integrated onto the SiFive FU540 and FU740 chip resp, and "sifive,spi0" + for the SiFive SPI v0 IP block with no chip integration tweaks. Please refer to sifive-blocks-ip-versioning.txt for details SPI RTL that corresponds to the IP block version numbers can be found here - From 7b14a272f9ac2438a85e59571fdb5a653d86430b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sun, 6 Dec 2020 10:18:15 +0900 Subject: [PATCH 117/127] dt-bindings: spi: dw-apb-ssi: Add Canaan K210 SPI controller Update the snps,dw-apb-ssi.yaml document to include the compatibility string "canaan,k210-spi" compatible string for the Canaan Kendryte K210 RISC-V SoC DW apb_ssi V4 SPI controller. Signed-off-by: Damien Le Moal Acked-by: Serge Semin Link: https://lore.kernel.org/r/20201206011817.11700-2-damien.lemoal@wdc.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 99ed9b416e94..4825157cd92e 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -65,6 +65,8 @@ properties: const: baikal,bt1-ssi - description: Baikal-T1 System Boot SPI Controller const: baikal,bt1-sys-ssi + - description: Canaan Kendryte K210 SoS SPI Controller + const: canaan,k210-spi reg: minItems: 1 From a51acc2400d47df0f87e1f011c63266421c594b9 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sun, 6 Dec 2020 10:18:16 +0900 Subject: [PATCH 118/127] spi: dw: Add support for 32-bits max xfer size The Synopsis DesignWare DW_apb_ssi specifications version 3.23 onward define a 32-bits maximum transfer size synthesis parameter (SSI_MAX_XFER_SIZE=32) in addition to the legacy 16-bits configuration (SSI_MAX_XFER_SIZE=16) for SPI controllers. When SSI_MAX_XFER_SIZE=32, the layout of the ctrlr0 register changes, moving the data frame format field from bits [3..0] to bits [16..20], and the RX/TX FIFO word size can be up to 32-bits. To support this new format, introduce the DW SPI capability flag DW_SPI_CAP_DFS32 to indicate that a controller is configured with SSI_MAX_XFER_SIZE=32. Since SSI_MAX_XFER_SIZE is a controller synthesis parameter not accessible through a register, the detection of this parameter value is done in spi_hw_init() by writing and reading the ctrlr0 register and testing the value of bits [3..0]. These bits are ignored (unchanged) for SSI_MAX_XFER_SIZE=16, allowing the detection. If a DFS32 capable SPI controller is detected, the new field dfs_offset in struct dw_spi is set to SPI_DFS32_OFFSET (16). dw_spi_update_config() is modified to set the data frame size field at the correct position is the CTRLR0 register, as indicated by the dfs_offset field of the dw_spi structure. The DW_SPI_CAP_DFS32 flag is also unconditionally set for SPI slave controllers, e.g. controllers that have the DW_SPI_CAP_DWC_SSI capability flag set. However, for these ssi controllers, the dfs_offset field is set to 0 as before (as per specifications). Finally, for any controller with the DW_SPI_CAP_DFS32 capability flag set, dw_spi_add_host() extends the value of bits_per_word_mask from 16-bits to 32-bits. dw_reader() and dw_writer() are also modified to handle 32-bits iTX/RX FIFO words. Suggested-by: Sean Anderson Signed-off-by: Damien Le Moal Acked-by: Serge Semin Link: https://lore.kernel.org/r/20201206011817.11700-3-damien.lemoal@wdc.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 44 ++++++++++++++++++++++++++++++++------- drivers/spi/spi-dw.h | 5 +++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index c33866f747db..a305074c482e 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -137,14 +137,16 @@ static inline u32 rx_max(struct dw_spi *dws) static void dw_writer(struct dw_spi *dws) { u32 max = tx_max(dws); - u16 txw = 0; + u32 txw = 0; while (max--) { if (dws->tx) { if (dws->n_bytes == 1) txw = *(u8 *)(dws->tx); - else + else if (dws->n_bytes == 2) txw = *(u16 *)(dws->tx); + else + txw = *(u32 *)(dws->tx); dws->tx += dws->n_bytes; } @@ -156,15 +158,17 @@ static void dw_writer(struct dw_spi *dws) static void dw_reader(struct dw_spi *dws) { u32 max = rx_max(dws); - u16 rxw; + u32 rxw; while (max--) { rxw = dw_read_io_reg(dws, DW_SPI_DR); if (dws->rx) { if (dws->n_bytes == 1) *(u8 *)(dws->rx) = rxw; - else + else if (dws->n_bytes == 2) *(u16 *)(dws->rx) = rxw; + else + *(u32 *)(dws->rx) = rxw; dws->rx += dws->n_bytes; } @@ -311,8 +315,8 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, u32 speed_hz; u16 clk_div; - /* CTRLR0[ 4/3: 0] Data Frame Size */ - cr0 |= (cfg->dfs - 1); + /* CTRLR0[ 4/3: 0] or CTRLR0[ 20: 16] Data Frame Size */ + cr0 |= (cfg->dfs - 1) << dws->dfs_offset; if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) /* CTRLR0[ 9:8] Transfer Mode */ @@ -828,6 +832,29 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); } + /* + * Detect CTRLR0.DFS field size and offset by testing the lowest bits + * writability. Note DWC SSI controller also has the extended DFS, but + * with zero offset. + */ + if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) { + u32 cr0, tmp = dw_readl(dws, DW_SPI_CTRLR0); + + spi_enable_chip(dws, 0); + dw_writel(dws, DW_SPI_CTRLR0, 0xffffffff); + cr0 = dw_readl(dws, DW_SPI_CTRLR0); + dw_writel(dws, DW_SPI_CTRLR0, tmp); + spi_enable_chip(dws, 1); + + if (!(cr0 & SPI_DFS_MASK)) { + dws->caps |= DW_SPI_CAP_DFS32; + dws->dfs_offset = SPI_DFS32_OFFSET; + dev_dbg(dev, "Detected 32-bits max data frame size\n"); + } + } else { + dws->caps |= DW_SPI_CAP_DFS32; + } + /* enable HW fixup for explicit CS deselect for Amazon's alpine chip */ if (dws->caps & DW_SPI_CAP_CS_OVERRIDE) dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF); @@ -864,7 +891,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->use_gpio_descriptors = true; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + if (dws->caps & DW_SPI_CAP_DFS32) + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + else + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index faf40cb66498..b665e040862c 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -9,6 +9,7 @@ #include #include #include +#include /* Register offsets */ #define DW_SPI_CTRLR0 0x00 @@ -41,6 +42,8 @@ /* Bit fields in CTRLR0 */ #define SPI_DFS_OFFSET 0 +#define SPI_DFS_MASK GENMASK(3, 0) +#define SPI_DFS32_OFFSET 16 #define SPI_FRF_OFFSET 4 #define SPI_FRF_SPI 0x0 @@ -121,6 +124,7 @@ enum dw_ssi_type { #define DW_SPI_CAP_CS_OVERRIDE BIT(0) #define DW_SPI_CAP_KEEMBAY_MST BIT(1) #define DW_SPI_CAP_DWC_SSI BIT(2) +#define DW_SPI_CAP_DFS32 BIT(3) /* Slave spi_transfer/spi_mem_op related */ struct dw_spi_cfg { @@ -148,6 +152,7 @@ struct dw_spi { unsigned long paddr; int irq; u32 fifo_len; /* depth of the FIFO buffer */ + unsigned int dfs_offset; /* CTRLR0 DFS field offset */ u32 max_mem_freq; /* max mem-ops bus freq */ u32 max_freq; /* max bus freq supported */ From b0dfd948379c79b8754e224e29b99d30ce0d79b8 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sun, 6 Dec 2020 10:18:17 +0900 Subject: [PATCH 119/127] spi: dw: Add support for the Canaan K210 SoC SPI The Canaan Kendryte K210 RISC-V SoC includes a DW apb_ssi v4 controller which is documented to have a 32 words deep TX and RX FIFO. The FIFO length detection in spi_hw_init() correctly detects this value. However, when the controller RX FIFO is filled up to 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. This likely due to a hardware bug which can be avoided by force setting the fifo_len field of struct dw_spi to 31. Define the dw_spi_canaan_k210_init() function to force set fifo_len to 31 when the device node compatible string is "canaan,k210-spi". Signed-off-by: Damien Le Moal Acked-by: Serge Semin Link: https://lore.kernel.org/r/20201206011817.11700-4-damien.lemoal@wdc.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index d0cc5bf4fa4e..17c06039a74d 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -222,6 +222,21 @@ static int dw_spi_keembay_init(struct platform_device *pdev, return 0; } +static int dw_spi_canaan_k210_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* + * The Canaan Kendryte K210 SoC DW apb_ssi v4 spi controller is + * documented to have a 32 word deep TX and RX FIFO, which + * spi_hw_init() detects. However, when the RX FIFO is filled up to + * 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. Avoid this + * problem by force setting fifo_len to 31. + */ + dwsmmio->dws.fifo_len = 31; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { int (*init_func)(struct platform_device *pdev, @@ -335,6 +350,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init}, { .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init}, + { .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); From 74639cbf51d7c0304342544a83dfda354a6bd208 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 4 Dec 2020 11:35:38 -0800 Subject: [PATCH 120/127] platform/chrome: cros_ec_spi: Don't overwrite spi::mode There isn't any need to overwrite the mode here in the driver with what has been detected by the firmware, such as DT or ACPI. In fact, if we use the SPI CS gpio descriptor feature we will overwrite the mode with SPI_MODE_0 where it already contains SPI_MODE_0 and more importantly SPI_CS_HIGH. Clearing the SPI_CS_HIGH bit causes the CS line to toggle when the device is probed when it shouldn't change, confusing the driver and making it fail to probe. Drop the assignment and let the spi core take care of it. Fixes: a17d94f0b6e1 ("mfd: Add ChromeOS EC SPI driver") Cc: Simon Glass Cc: Gwendal Grignou Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Acked-by: Enric Balletbo i Serra Cc: Alexandru M Stan Signed-off-by: Stephen Boyd Reviewed-by: Simon Glass Link: https://lore.kernel.org/r/20201204193540.3047030-2-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/platform/chrome/cros_ec_spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index dfa1f816a45f..f9df218fc2bb 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -742,7 +742,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) int err; spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; spi->rt = true; err = spi_setup(spi); if (err < 0) From 7a5172b7990d97ab9ef64e6d9063aa68099ea023 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 4 Dec 2020 11:35:39 -0800 Subject: [PATCH 121/127] platform/chrome: cros_ec_spi: Drop bits_per_word assignment This is already handed by default in spi_setup() if the bits_per_word is 0, so just drop it to shave off a line. Cc: Simon Glass Cc: Gwendal Grignou Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Acked-by: Enric Balletbo i Serra Cc: Alexandru M Stan Signed-off-by: Stephen Boyd Reviewed-by: Simon Glass Link: https://lore.kernel.org/r/20201204193540.3047030-3-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/platform/chrome/cros_ec_spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index f9df218fc2bb..14c4046fa04d 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -741,7 +741,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) struct cros_ec_spi *ec_spi; int err; - spi->bits_per_word = 8; spi->rt = true; err = spi_setup(spi); if (err < 0) From 3b25f337929e73232f0aa990cd68a129f53652e2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 4 Dec 2020 11:35:40 -0800 Subject: [PATCH 122/127] spi: spi-geni-qcom: Use the new method of gpio CS control Let's set the 'use_gpio_descriptors' field so that we use the new way of requesting the CS GPIOs in the core. This allows us to avoid having to configure the CS pins in "output" mode with an 'output-enable' pinctrl setting. Cc: Akash Asthana Cc: Bjorn Andersson Reviewed-by: Douglas Anderson Cc: Alexandru M Stan Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20201204193540.3047030-4-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 25810a7eef10..c4c88984abc9 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -636,6 +636,7 @@ static int spi_geni_probe(struct platform_device *pdev) spi->auto_runtime_pm = true; spi->handle_err = handle_fifo_timeout; spi->set_cs = spi_geni_set_cs; + spi->use_gpio_descriptors = true; init_completion(&mas->cs_done); init_completion(&mas->cancel_done); From 0da6736ecd10b45e535b100acd58df2db4c099d8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 10 Dec 2020 12:21:39 +0100 Subject: [PATCH 123/127] regulator: max14577: Add proper module aliases strings Add proper modalias structures to let this driver load automatically if compiled as module, because max14577 MFD driver creates MFD cells with such compatible strings. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201210112139.5370-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- drivers/regulator/max14577-regulator.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index e34face736f4..1d78b455cc48 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit); MODULE_AUTHOR("Krzysztof Kozlowski "); MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max14577-regulator"); +MODULE_ALIAS("platform:max77836-regulator"); From 9326e4f1e5dd1a4410c429638d3c412b6fc17040 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Wed, 9 Dec 2020 19:35:14 +0200 Subject: [PATCH 124/127] spi: Limit the spi device max speed to controller's max speed Make sure the max_speed_hz of spi_device does not override the max_speed_hz of controller. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201209173514.93328-1-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index fc9a59788d2e..45021d271da2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3369,7 +3369,8 @@ int spi_setup(struct spi_device *spi) if (status) return status; - if (!spi->max_speed_hz) + if (!spi->max_speed_hz || + spi->max_speed_hz > spi->controller->max_speed_hz) spi->max_speed_hz = spi->controller->max_speed_hz; mutex_lock(&spi->controller->io_mutex); From 6a6939d5f588b40db32b82ebcec20ee5189c8376 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 10 Dec 2020 18:27:48 -0300 Subject: [PATCH 125/127] regulator: pfuze100: Convert the driver to DT-only Since 5.10-rc1 i.MX is a devicetree-only platform, so simplify the code by removing the unused non-DT support. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201210212748.5849-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- drivers/regulator/pfuze100-regulator.c | 34 +------------------------- include/linux/regulator/pfuze100.h | 6 ----- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7faedb05b7a6..cb9333d4c0fd 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -830,7 +830,7 @@ config REGULATOR_PF8X00 config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" - depends on I2C + depends on I2C && OF select REGMAP_I2C help Say y here to support the regulators found on the Freescale diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 7e8ba9246167..09bab0b45f90 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = { 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, }; -static const struct i2c_device_id pfuze_device_id[] = { - {.name = "pfuze100", .driver_data = PFUZE100}, - {.name = "pfuze200", .driver_data = PFUZE200}, - {.name = "pfuze3000", .driver_data = PFUZE3000}, - {.name = "pfuze3001", .driver_data = PFUZE3001}, - { } -}; -MODULE_DEVICE_TABLE(i2c, pfuze_device_id); - static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, @@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = { PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; -#ifdef CONFIG_OF /* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { { .name = "sw1ab", }, @@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index) { return pfuze_matches[index].of_node; } -#else -static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) -{ - return 0; -} - -static inline struct regulator_init_data *match_init_data(int index) -{ - return NULL; -} - -static inline struct device_node *match_of_node(int index) -{ - return NULL; -} -#endif static struct pfuze_chip *syspm_pfuze_chip; @@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pfuze_chip *pfuze_chip; - struct pfuze_regulator_platform_data *pdata = - dev_get_platdata(&client->dev); struct regulator_config config = { }; int i, ret; const struct of_device_id *match; @@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, desc = &pfuze_chip->regulator_descs[i].desc; - if (pdata) - init_data = pdata->init_data[i]; - else - init_data = match_init_data(i); + init_data = match_init_data(i); /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { @@ -876,7 +845,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client) } static struct i2c_driver pfuze_driver = { - .id_table = pfuze_device_id, .driver = { .name = "pfuze100-regulator", .of_match_table = pfuze_dt_ids, diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index d47e668d9ca8..c964fe8ab698 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -63,10 +63,4 @@ #define PFUZE3001_VLDO3 8 #define PFUZE3001_VLDO4 9 -struct regulator_init_data; - -struct pfuze_regulator_platform_data { - struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR]; -}; - #endif /* __LINUX_REG_PFUZE100_H */ From 2819569147cb6e79730f2907d3ab3dfe75fe8478 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Fri, 11 Dec 2020 16:45:10 +0800 Subject: [PATCH 126/127] regulator: mc13892-regulator: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201211084510.2264-1-zhengyongjun3@huawei.com Signed-off-by: Mark Brown --- drivers/regulator/mc13892-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index a731e826a037..5221f7a9df91 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev) /* update mc13892_vcam ops */ memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops, sizeof(struct regulator_ops)); - mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode, - mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode, + mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode; + mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode; mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops; mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators, From 2fee9583198eb97b5351feda7bd825e0f778385c Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Wed, 9 Dec 2020 10:57:44 +0100 Subject: [PATCH 127/127] spi: dt-bindings: clarify CS behavior for spi-cs-high and gpio descriptors Behavior of CS signal in combination of spi-cs-high and gpio descriptors is not clearly defined and documented. So clarify the documentation Cc: linus.walleij@linaro.org Cc: linux-gpio@vger.kernel.org Signed-off-by: H. Nikolaus Schaller Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/3bed61807fff6268789e7d411412fbc5cd6ffe2a.1607507863.git.hns@goldelico.com Signed-off-by: Mark Brown --- .../bindings/spi/spi-controller.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-controller.yaml b/Documentation/devicetree/bindings/spi/spi-controller.yaml index 1b56d5e40f1f..5f505810104d 100644 --- a/Documentation/devicetree/bindings/spi/spi-controller.yaml +++ b/Documentation/devicetree/bindings/spi/spi-controller.yaml @@ -42,6 +42,33 @@ properties: cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 + The second flag of a gpio descriptor can be GPIO_ACTIVE_HIGH (0) + or GPIO_ACTIVE_LOW(1). Legacy device trees often use 0. + + There is a special rule set for combining the second flag of an + cs-gpio with the optional spi-cs-high flag for SPI slaves. + + Each table entry defines how the CS pin is to be physically + driven (not considering potential gpio inversions by pinmux): + + device node | cs-gpio | CS pin state active | Note + ================+===============+=====================+===== + spi-cs-high | - | H | + - | - | L | + spi-cs-high | ACTIVE_HIGH | H | + - | ACTIVE_HIGH | L | 1 + spi-cs-high | ACTIVE_LOW | H | 2 + - | ACTIVE_LOW | L | + + Notes: + 1) Should print a warning about polarity inversion. + Here it would be wise to avoid and define the gpio as + ACTIVE_LOW. + 2) Should print a warning about polarity inversion + because ACTIVE_LOW is overridden by spi-cs-high. + Should be generally avoided and be replaced by + spi-cs-high + ACTIVE_HIGH. + num-cs: $ref: /schemas/types.yaml#/definitions/uint32 description: