1 // SPDX-License-Identifier: GPL-2.0
3 * DesignWare PWM Controller driver (PCI part)
5 * Copyright (C) 2018-2020 Intel Corporation
7 * Author: Felipe Balbi (Intel)
8 * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
9 * Author: Raymond Tan <raymond.tan@intel.com>
12 * - The hardware cannot generate a 0 % or 100 % duty cycle. Both high and low
13 * periods are one or more input clock periods long.
16 #define DEFAULT_MOUDLE_NAMESPACE dwc_pwm
18 #include <linux/bitops.h>
19 #include <linux/export.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/pci.h>
23 #include <linux/pm_runtime.h>
24 #include <linux/pwm.h>
29 static const struct dwc_pwm_info ehl_pwm_info
= {
34 static int dwc_pwm_init_one(struct device
*dev
, struct dwc_pwm_drvdata
*ddata
, unsigned int idx
)
36 struct pwm_chip
*chip
;
40 chip
= dwc_pwm_alloc(dev
);
44 dwc
= to_dwc_pwm(chip
);
45 dwc
->base
= ddata
->io_base
+ (ddata
->info
->size
* idx
);
47 ret
= devm_pwmchip_add(dev
, chip
);
51 ddata
->chips
[idx
] = chip
;
55 static int dwc_pwm_probe(struct pci_dev
*pci
, const struct pci_device_id
*id
)
57 const struct dwc_pwm_info
*info
;
58 struct device
*dev
= &pci
->dev
;
59 struct dwc_pwm_drvdata
*ddata
;
63 ret
= pcim_enable_device(pci
);
65 return dev_err_probe(dev
, ret
, "Failed to enable device\n");
69 ret
= pcim_iomap_regions(pci
, BIT(0), pci_name(pci
));
71 return dev_err_probe(dev
, ret
, "Failed to iomap PCI BAR\n");
73 info
= (const struct dwc_pwm_info
*)id
->driver_data
;
74 ddata
= devm_kzalloc(dev
, struct_size(ddata
, chips
, info
->nr
), GFP_KERNEL
);
79 * No need to check for pcim_iomap_table() failure,
80 * pcim_iomap_regions() already does it for us.
82 ddata
->io_base
= pcim_iomap_table(pci
)[0];
85 for (idx
= 0; idx
< ddata
->info
->nr
; idx
++) {
86 ret
= dwc_pwm_init_one(dev
, ddata
, idx
);
91 dev_set_drvdata(dev
, ddata
);
94 pm_runtime_allow(dev
);
99 static void dwc_pwm_remove(struct pci_dev
*pci
)
101 pm_runtime_forbid(&pci
->dev
);
102 pm_runtime_get_noresume(&pci
->dev
);
105 static int dwc_pwm_suspend(struct device
*dev
)
107 struct dwc_pwm_drvdata
*ddata
= dev_get_drvdata(dev
);
110 for (idx
= 0; idx
< ddata
->info
->nr
; idx
++) {
111 struct pwm_chip
*chip
= ddata
->chips
[idx
];
112 struct dwc_pwm
*dwc
= to_dwc_pwm(chip
);
115 for (i
= 0; i
< DWC_TIMERS_TOTAL
; i
++) {
116 if (chip
->pwms
[i
].state
.enabled
) {
117 dev_err(dev
, "PWM %u in use by consumer (%s)\n",
118 i
, chip
->pwms
[i
].label
);
121 dwc
->ctx
[i
].cnt
= dwc_pwm_readl(dwc
, DWC_TIM_LD_CNT(i
));
122 dwc
->ctx
[i
].cnt2
= dwc_pwm_readl(dwc
, DWC_TIM_LD_CNT2(i
));
123 dwc
->ctx
[i
].ctrl
= dwc_pwm_readl(dwc
, DWC_TIM_CTRL(i
));
130 static int dwc_pwm_resume(struct device
*dev
)
132 struct dwc_pwm_drvdata
*ddata
= dev_get_drvdata(dev
);
135 for (idx
= 0; idx
< ddata
->info
->nr
; idx
++) {
136 struct pwm_chip
*chip
= ddata
->chips
[idx
];
137 struct dwc_pwm
*dwc
= to_dwc_pwm(chip
);
140 for (i
= 0; i
< DWC_TIMERS_TOTAL
; i
++) {
141 dwc_pwm_writel(dwc
, dwc
->ctx
[i
].cnt
, DWC_TIM_LD_CNT(i
));
142 dwc_pwm_writel(dwc
, dwc
->ctx
[i
].cnt2
, DWC_TIM_LD_CNT2(i
));
143 dwc_pwm_writel(dwc
, dwc
->ctx
[i
].ctrl
, DWC_TIM_CTRL(i
));
150 static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops
, dwc_pwm_suspend
, dwc_pwm_resume
);
152 static const struct pci_device_id dwc_pwm_id_table
[] = {
153 { PCI_VDEVICE(INTEL
, 0x4bb7), (kernel_ulong_t
)&ehl_pwm_info
},
154 { } /* Terminating Entry */
156 MODULE_DEVICE_TABLE(pci
, dwc_pwm_id_table
);
158 static struct pci_driver dwc_pwm_driver
= {
160 .probe
= dwc_pwm_probe
,
161 .remove
= dwc_pwm_remove
,
162 .id_table
= dwc_pwm_id_table
,
164 .pm
= pm_sleep_ptr(&dwc_pwm_pm_ops
),
168 module_pci_driver(dwc_pwm_driver
);
170 MODULE_AUTHOR("Felipe Balbi (Intel)");
171 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
172 MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
173 MODULE_DESCRIPTION("DesignWare PWM Controller");
174 MODULE_LICENSE("GPL");