Merge tag 'spi-fix-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Aug 2022 18:40:15 +0000 (11:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Aug 2022 18:40:15 +0000 (11:40 -0700)
Pull spi fixes from Mark Brown:
 "A few fixes that came in since my pull request, the Meson fix is a
  little large since it's fixing all possible cases of the problem that
  was observed with the driver and clock API trying to share
  configuration by integrating the device clocking fully with the clock
  API rather than spot fixing the one instance that was observed"

* tag 'spi-fix-v6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi:
  spi: dt-bindings: Drop Pratyush Yadav
  spi: meson-spicc: add local pow2 clock ops to preserve rate between messages
  MAINTAINERS: rectify entry for ARM/HPE GXP ARCHITECTURE
  spi: spi.c: Add missing __percpu annotations in users of spi_statistics

Documentation/devicetree/bindings/spi/cdns,qspi-nor-peripheral-props.yaml
Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml
Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
MAINTAINERS
drivers/spi/spi-meson-spicc.c
drivers/spi/spi.c

index 553601a441a7dc2a2a0cf3f87b0357e4861222bd..510b82c177c059048bb3b900a966870f329551db 100644 (file)
@@ -10,7 +10,7 @@ description:
   See spi-peripheral-props.yaml for more info.
 
 maintainers:
-  - Pratyush Yadav <p.yadav@ti.com>
+  - Vaishnav Achath <vaishnav.a@ti.com>
 
 properties:
   # cdns,qspi-nor.yaml
index 0a537fa3a6410cf0f28bce7317aadd3d0c2300fb..4707294d8f596888eb1a3063106bcfe71ce237c0 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Cadence Quad SPI controller
 
 maintainers:
-  - Pratyush Yadav <p.yadav@ti.com>
+  - Vaishnav Achath <vaishnav.a@ti.com>
 
 allOf:
   - $ref: spi-controller.yaml#
index ce048e782e80422d2a5474b2c94262ac55e6abc1..a4abe15880053419cb0926330c5955d4cbf8e529 100644 (file)
@@ -16,7 +16,7 @@ description:
   their own separate schema that should be referenced from here.
 
 maintainers:
-  - Pratyush Yadav <p.yadav@ti.com>
+  - Mark Brown <broonie@kernel.org>
 
 properties:
   reg:
index 8a5012ba6ff98ac6f4a1415b3fce51423329c2d2..f512b430c7cbb8835e9ff3bd66687398c4345b93 100644 (file)
@@ -2178,7 +2178,7 @@ M:        Jean-Marie Verdun <verdun@hpe.com>
 M:     Nick Hawkins <nick.hawkins@hpe.com>
 S:     Maintained
 F:     Documentation/devicetree/bindings/arm/hpe,gxp.yaml
-F:     Documentation/devicetree/bindings/spi/hpe,gxp-spi.yaml
+F:     Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
 F:     Documentation/devicetree/bindings/timer/hpe,gxp-timer.yaml
 F:     arch/arm/boot/dts/hpe-bmc*
 F:     arch/arm/boot/dts/hpe-gxp*
index 0bc7daa7afc83d0d20424800845bb16f1c2c473f..e4cb52e1fe2619d7699413d05d5dacc1cbf32a0e 100644 (file)
@@ -156,6 +156,7 @@ struct meson_spicc_device {
        void __iomem                    *base;
        struct clk                      *core;
        struct clk                      *pclk;
+       struct clk_divider              pow2_div;
        struct clk                      *clk;
        struct spi_message              *message;
        struct spi_transfer             *xfer;
@@ -168,6 +169,8 @@ struct meson_spicc_device {
        unsigned long                   xfer_remain;
 };
 
+#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
+
 static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
 {
        u32 conf;
@@ -421,7 +424,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
 {
        struct meson_spicc_device *spicc = spi_master_get_devdata(master);
        struct spi_device *spi = message->spi;
-       u32 conf = 0;
+       u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
 
        /* Store current message */
        spicc->message = message;
@@ -458,8 +461,6 @@ static int meson_spicc_prepare_message(struct spi_master *master,
        /* Select CS */
        conf |= FIELD_PREP(SPICC_CS_MASK, spi->chip_select);
 
-       /* Default Clock rate core/4 */
-
        /* Default 8bit word */
        conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1);
 
@@ -476,12 +477,16 @@ static int meson_spicc_prepare_message(struct spi_master *master,
 static int meson_spicc_unprepare_transfer(struct spi_master *master)
 {
        struct meson_spicc_device *spicc = spi_master_get_devdata(master);
+       u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
 
        /* Disable all IRQs */
        writel(0, spicc->base + SPICC_INTREG);
 
        device_reset_optional(&spicc->pdev->dev);
 
+       /* Set default configuration, keeping datarate field */
+       writel_relaxed(conf, spicc->base + SPICC_CONREG);
+
        return 0;
 }
 
@@ -518,14 +523,60 @@ static void meson_spicc_cleanup(struct spi_device *spi)
  * Clk path for G12A series:
  *    pclk -> pow2 fixed div -> pow2 div -> mux -> out
  *    pclk -> enh fixed div -> enh div -> mux -> out
+ *
+ * The pow2 divider is tied to the controller HW state, and the
+ * divider is only valid when the controller is initialized.
+ *
+ * A set of clock ops is added to make sure we don't read/set this
+ * clock rate while the controller is in an unknown state.
  */
 
-static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
+static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw,
+                                                 unsigned long parent_rate)
+{
+       struct clk_divider *divider = to_clk_divider(hw);
+       struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
+
+       if (!spicc->master->cur_msg || !spicc->master->busy)
+               return 0;
+
+       return clk_divider_ops.recalc_rate(hw, parent_rate);
+}
+
+static int meson_spicc_pow2_determine_rate(struct clk_hw *hw,
+                                          struct clk_rate_request *req)
+{
+       struct clk_divider *divider = to_clk_divider(hw);
+       struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
+
+       if (!spicc->master->cur_msg || !spicc->master->busy)
+               return -EINVAL;
+
+       return clk_divider_ops.determine_rate(hw, req);
+}
+
+static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long parent_rate)
+{
+       struct clk_divider *divider = to_clk_divider(hw);
+       struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
+
+       if (!spicc->master->cur_msg || !spicc->master->busy)
+               return -EINVAL;
+
+       return clk_divider_ops.set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops meson_spicc_pow2_clk_ops = {
+       .recalc_rate = meson_spicc_pow2_recalc_rate,
+       .determine_rate = meson_spicc_pow2_determine_rate,
+       .set_rate = meson_spicc_pow2_set_rate,
+};
+
+static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
 {
        struct device *dev = &spicc->pdev->dev;
-       struct clk_fixed_factor *pow2_fixed_div, *enh_fixed_div;
-       struct clk_divider *pow2_div, *enh_div;
-       struct clk_mux *mux;
+       struct clk_fixed_factor *pow2_fixed_div;
        struct clk_init_data init;
        struct clk *clk;
        struct clk_parent_data parent_data[2];
@@ -560,31 +611,45 @@ static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
        if (WARN_ON(IS_ERR(clk)))
                return PTR_ERR(clk);
 
-       pow2_div = devm_kzalloc(dev, sizeof(*pow2_div), GFP_KERNEL);
-       if (!pow2_div)
-               return -ENOMEM;
-
        snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev));
        init.name = name;
-       init.ops = &clk_divider_ops;
-       init.flags = CLK_SET_RATE_PARENT;
+       init.ops = &meson_spicc_pow2_clk_ops;
+       /*
+        * Set NOCACHE here to make sure we read the actual HW value
+        * since we reset the HW after each transfer.
+        */
+       init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE;
        parent_data[0].hw = &pow2_fixed_div->hw;
        init.num_parents = 1;
 
-       pow2_div->shift = 16,
-       pow2_div->width = 3,
-       pow2_div->flags = CLK_DIVIDER_POWER_OF_TWO,
-       pow2_div->reg = spicc->base + SPICC_CONREG;
-       pow2_div->hw.init = &init;
+       spicc->pow2_div.shift = 16,
+       spicc->pow2_div.width = 3,
+       spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO,
+       spicc->pow2_div.reg = spicc->base + SPICC_CONREG;
+       spicc->pow2_div.hw.init = &init;
 
-       clk = devm_clk_register(dev, &pow2_div->hw);
-       if (WARN_ON(IS_ERR(clk)))
-               return PTR_ERR(clk);
+       spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw);
+       if (WARN_ON(IS_ERR(spicc->clk)))
+               return PTR_ERR(spicc->clk);
 
-       if (!spicc->data->has_enhance_clk_div) {
-               spicc->clk = clk;
-               return 0;
-       }
+       return 0;
+}
+
+static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
+{
+       struct device *dev = &spicc->pdev->dev;
+       struct clk_fixed_factor *enh_fixed_div;
+       struct clk_divider *enh_div;
+       struct clk_mux *mux;
+       struct clk_init_data init;
+       struct clk *clk;
+       struct clk_parent_data parent_data[2];
+       char name[64];
+
+       memset(&init, 0, sizeof(init));
+       memset(&parent_data, 0, sizeof(parent_data));
+
+       init.parent_data = parent_data;
 
        /* algorithm for enh div: rate = freq / 2 / (N + 1) */
 
@@ -637,7 +702,7 @@ static int meson_spicc_clk_init(struct meson_spicc_device *spicc)
        snprintf(name, sizeof(name), "%s#sel", dev_name(dev));
        init.name = name;
        init.ops = &clk_mux_ops;
-       parent_data[0].hw = &pow2_div->hw;
+       parent_data[0].hw = &spicc->pow2_div.hw;
        parent_data[1].hw = &enh_div->hw;
        init.num_parents = 2;
        init.flags = CLK_SET_RATE_PARENT;
@@ -754,12 +819,20 @@ static int meson_spicc_probe(struct platform_device *pdev)
 
        meson_spicc_oen_enable(spicc);
 
-       ret = meson_spicc_clk_init(spicc);
+       ret = meson_spicc_pow2_clk_init(spicc);
        if (ret) {
-               dev_err(&pdev->dev, "clock registration failed\n");
+               dev_err(&pdev->dev, "pow2 clock registration failed\n");
                goto out_clk;
        }
 
+       if (spicc->data->has_enhance_clk_div) {
+               ret = meson_spicc_enh_clk_init(spicc);
+               if (ret) {
+                       dev_err(&pdev->dev, "clock registration failed\n");
+                       goto out_clk;
+               }
+       }
+
        ret = devm_spi_register_master(&pdev->dev, master);
        if (ret) {
                dev_err(&pdev->dev, "spi master registration failed\n");
index 8f97a3eacdeab01d738036593faee2ac039988e2..83da8862b8f22ee414bc7f92af8550285e43e4d1 100644 (file)
@@ -95,7 +95,7 @@ static ssize_t driver_override_show(struct device *dev,
 }
 static DEVICE_ATTR_RW(driver_override);
 
-static struct spi_statistics *spi_alloc_pcpu_stats(struct device *dev)
+static struct spi_statistics __percpu *spi_alloc_pcpu_stats(struct device *dev)
 {
        struct spi_statistics __percpu *pcpu_stats;
 
@@ -162,7 +162,7 @@ static struct device_attribute dev_attr_spi_device_##field = {              \
 }
 
 #define SPI_STATISTICS_SHOW_NAME(name, file, field)                    \
-static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
+static ssize_t spi_statistics_##name##_show(struct spi_statistics __percpu *stat, \
                                            char *buf)                  \
 {                                                                      \
        ssize_t len;                                                    \
@@ -309,7 +309,7 @@ static const struct attribute_group *spi_master_groups[] = {
        NULL,
 };
 
-static void spi_statistics_add_transfer_stats(struct spi_statistics *pcpu_stats,
+static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pcpu_stats,
                                              struct spi_transfer *xfer,
                                              struct spi_controller *ctlr)
 {
@@ -1275,8 +1275,8 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
                             struct spi_message *msg,
                             struct spi_transfer *xfer)
 {
-       struct spi_statistics *statm = ctlr->pcpu_statistics;
-       struct spi_statistics *stats = msg->spi->pcpu_statistics;
+       struct spi_statistics __percpu *statm = ctlr->pcpu_statistics;
+       struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics;
        u32 speed_hz = xfer->speed_hz;
        unsigned long long ms;
 
@@ -1432,8 +1432,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
        struct spi_transfer *xfer;
        bool keep_cs = false;
        int ret = 0;
-       struct spi_statistics *statm = ctlr->pcpu_statistics;
-       struct spi_statistics *stats = msg->spi->pcpu_statistics;
+       struct spi_statistics __percpu *statm = ctlr->pcpu_statistics;
+       struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics;
 
        spi_set_cs(msg->spi, true, false);