pwm: dwc: allow suspend/resume for 16 channels
authorRaag Jadav <raag.jadav@intel.com>
Mon, 15 Apr 2024 07:40:51 +0000 (13:10 +0530)
committerUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Mon, 15 Apr 2024 15:28:13 +0000 (17:28 +0200)
With 16 channel pwm support, we're registering two instances of pwm_chip
with 8 channels each. We need to update PM functions to use both instances
of pwm_chip during power state transitions.

Introduce struct dwc_pwm_drvdata and use it as driver_data, which will
maintain both instances of pwm_chip along with dwc_pwm_info and allow us
to use them inside suspend/resume handles.

Fixes: ebf2c89eb95e ("pwm: dwc: Add 16 channel support for Intel Elkhart Lake")
Signed-off-by: Raag Jadav <raag.jadav@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20240415074051.14681-1-raag.jadav@intel.com
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
drivers/pwm/pwm-dwc-core.c
drivers/pwm/pwm-dwc.c
drivers/pwm/pwm-dwc.h

index 043736972cb9216c59a7cb3bc6682e056cdb2373..c8425493b95d855a7562406501b7c803ef481b22 100644 (file)
@@ -172,7 +172,6 @@ struct pwm_chip *dwc_pwm_alloc(struct device *dev)
        dwc->clk_ns = 10;
        chip->ops = &dwc_pwm_ops;
 
-       dev_set_drvdata(dev, chip);
        return chip;
 }
 EXPORT_SYMBOL_GPL(dwc_pwm_alloc);
index 676eaf8d7a53f76672527c1871a306cbcdb9b7ba..fb3eadf6fbc464773b17c30235c51f5a4ff6917f 100644 (file)
@@ -31,26 +31,34 @@ static const struct dwc_pwm_info ehl_pwm_info = {
        .size = 0x1000,
 };
 
-static int dwc_pwm_init_one(struct device *dev, void __iomem *base, unsigned int offset)
+static int dwc_pwm_init_one(struct device *dev, struct dwc_pwm_drvdata *ddata, unsigned int idx)
 {
        struct pwm_chip *chip;
        struct dwc_pwm *dwc;
+       int ret;
 
        chip = dwc_pwm_alloc(dev);
        if (IS_ERR(chip))
                return PTR_ERR(chip);
 
        dwc = to_dwc_pwm(chip);
-       dwc->base = base + offset;
+       dwc->base = ddata->io_base + (ddata->info->size * idx);
 
-       return devm_pwmchip_add(dev, chip);
+       ret = devm_pwmchip_add(dev, chip);
+       if (ret)
+               return ret;
+
+       ddata->chips[idx] = chip;
+       return 0;
 }
 
 static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
 {
        const struct dwc_pwm_info *info;
        struct device *dev = &pci->dev;
-       int i, ret;
+       struct dwc_pwm_drvdata *ddata;
+       unsigned int idx;
+       int ret;
 
        ret = pcim_enable_device(pci);
        if (ret)
@@ -63,17 +71,25 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
                return dev_err_probe(dev, ret, "Failed to iomap PCI BAR\n");
 
        info = (const struct dwc_pwm_info *)id->driver_data;
-
-       for (i = 0; i < info->nr; i++) {
-               /*
-                * No need to check for pcim_iomap_table() failure,
-                * pcim_iomap_regions() already does it for us.
-                */
-               ret = dwc_pwm_init_one(dev, pcim_iomap_table(pci)[0], i * info->size);
+       ddata = devm_kzalloc(dev, struct_size(ddata, chips, info->nr), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       /*
+        * No need to check for pcim_iomap_table() failure,
+        * pcim_iomap_regions() already does it for us.
+        */
+       ddata->io_base = pcim_iomap_table(pci)[0];
+       ddata->info = info;
+
+       for (idx = 0; idx < ddata->info->nr; idx++) {
+               ret = dwc_pwm_init_one(dev, ddata, idx);
                if (ret)
                        return ret;
        }
 
+       dev_set_drvdata(dev, ddata);
+
        pm_runtime_put(dev);
        pm_runtime_allow(dev);
 
@@ -88,19 +104,24 @@ static void dwc_pwm_remove(struct pci_dev *pci)
 
 static int dwc_pwm_suspend(struct device *dev)
 {
-       struct pwm_chip *chip = dev_get_drvdata(dev);
-       struct dwc_pwm *dwc = to_dwc_pwm(chip);
-       int i;
-
-       for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
-               if (chip->pwms[i].state.enabled) {
-                       dev_err(dev, "PWM %u in use by consumer (%s)\n",
-                               i, chip->pwms[i].label);
-                       return -EBUSY;
+       struct dwc_pwm_drvdata *ddata = dev_get_drvdata(dev);
+       unsigned int idx;
+
+       for (idx = 0; idx < ddata->info->nr; idx++) {
+               struct pwm_chip *chip = ddata->chips[idx];
+               struct dwc_pwm *dwc = to_dwc_pwm(chip);
+               unsigned int i;
+
+               for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
+                       if (chip->pwms[i].state.enabled) {
+                               dev_err(dev, "PWM %u in use by consumer (%s)\n",
+                                       i, chip->pwms[i].label);
+                               return -EBUSY;
+                       }
+                       dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i));
+                       dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i));
+                       dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i));
                }
-               dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i));
-               dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i));
-               dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i));
        }
 
        return 0;
@@ -108,14 +129,19 @@ static int dwc_pwm_suspend(struct device *dev)
 
 static int dwc_pwm_resume(struct device *dev)
 {
-       struct pwm_chip *chip = dev_get_drvdata(dev);
-       struct dwc_pwm *dwc = to_dwc_pwm(chip);
-       int i;
-
-       for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
-               dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i));
-               dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i));
-               dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i));
+       struct dwc_pwm_drvdata *ddata = dev_get_drvdata(dev);
+       unsigned int idx;
+
+       for (idx = 0; idx < ddata->info->nr; idx++) {
+               struct pwm_chip *chip = ddata->chips[idx];
+               struct dwc_pwm *dwc = to_dwc_pwm(chip);
+               unsigned int i;
+
+               for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
+                       dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i));
+                       dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i));
+                       dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i));
+               }
        }
 
        return 0;
index a8b074841ae8054a5a3737127442a1d0e9979e02..c6e2df5a61227131c50fc3c6351326217371c3a3 100644 (file)
@@ -38,6 +38,12 @@ struct dwc_pwm_info {
        unsigned int size;
 };
 
+struct dwc_pwm_drvdata {
+       const struct dwc_pwm_info *info;
+       void __iomem *io_base;
+       struct pwm_chip *chips[];
+};
+
 struct dwc_pwm_ctx {
        u32 cnt;
        u32 cnt2;