|
@@ -19,6 +19,7 @@
|
|
#include <linux/clk.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/io.h>
|
|
#include <linux/pwm.h>
|
|
#include <linux/pwm.h>
|
|
|
|
+#include <linux/of_device.h>
|
|
|
|
|
|
#include <asm/div64.h>
|
|
#include <asm/div64.h>
|
|
|
|
|
|
@@ -124,6 +125,46 @@ static struct pwm_ops pxa_pwm_ops = {
|
|
.owner = THIS_MODULE,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+#ifdef CONFIG_OF
|
|
|
|
+/*
|
|
|
|
+ * Device tree users must create one device instance for each pwm channel.
|
|
|
|
+ * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
|
|
|
|
+ * code that this is a single channel pxa25x-pwm. Currently all devices are
|
|
|
|
+ * supported identically.
|
|
|
|
+ */
|
|
|
|
+static struct of_device_id pwm_of_match[] = {
|
|
|
|
+ { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0]},
|
|
|
|
+ { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0]},
|
|
|
|
+ { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0]},
|
|
|
|
+ { .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0]},
|
|
|
|
+ { }
|
|
|
|
+};
|
|
|
|
+MODULE_DEVICE_TABLE(of, pwm_of_match);
|
|
|
|
+#else
|
|
|
|
+#define pwm_of_match NULL
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static const struct platform_device_id *pxa_pwm_get_id_dt(struct device *dev)
|
|
|
|
+{
|
|
|
|
+ const struct of_device_id *id = of_match_device(pwm_of_match, dev);
|
|
|
|
+
|
|
|
|
+ return id ? id->data : NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct pwm_device *
|
|
|
|
+pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
|
|
|
|
+{
|
|
|
|
+ struct pwm_device *pwm;
|
|
|
|
+
|
|
|
|
+ pwm = pwm_request_from_chip(pc, 0, NULL);
|
|
|
|
+ if (IS_ERR(pwm))
|
|
|
|
+ return pwm;
|
|
|
|
+
|
|
|
|
+ pwm_set_period(pwm, args->args[0]);
|
|
|
|
+
|
|
|
|
+ return pwm;
|
|
|
|
+}
|
|
|
|
+
|
|
static int pwm_probe(struct platform_device *pdev)
|
|
static int pwm_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
const struct platform_device_id *id = platform_get_device_id(pdev);
|
|
const struct platform_device_id *id = platform_get_device_id(pdev);
|
|
@@ -131,6 +172,12 @@ static int pwm_probe(struct platform_device *pdev)
|
|
struct resource *r;
|
|
struct resource *r;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
|
|
+ if (IS_ENABLED(CONFIG_OF) && id == NULL)
|
|
|
|
+ id = pxa_pwm_get_id_dt(&pdev->dev);
|
|
|
|
+
|
|
|
|
+ if (id == NULL)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
|
|
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
|
|
if (pwm == NULL) {
|
|
if (pwm == NULL) {
|
|
dev_err(&pdev->dev, "failed to allocate memory\n");
|
|
dev_err(&pdev->dev, "failed to allocate memory\n");
|
|
@@ -146,6 +193,11 @@ static int pwm_probe(struct platform_device *pdev)
|
|
pwm->chip.base = -1;
|
|
pwm->chip.base = -1;
|
|
pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
|
|
pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1;
|
|
|
|
|
|
|
|
+ if (IS_ENABLED(CONFIG_OF)) {
|
|
|
|
+ pwm->chip.of_xlate = pxa_pwm_of_xlate;
|
|
|
|
+ pwm->chip.of_pwm_n_cells = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
|
pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
|
if (IS_ERR(pwm->mmio_base))
|
|
if (IS_ERR(pwm->mmio_base))
|
|
@@ -176,6 +228,7 @@ static struct platform_driver pwm_driver = {
|
|
.driver = {
|
|
.driver = {
|
|
.name = "pxa25x-pwm",
|
|
.name = "pxa25x-pwm",
|
|
.owner = THIS_MODULE,
|
|
.owner = THIS_MODULE,
|
|
|
|
+ .of_match_table = pwm_of_match,
|
|
},
|
|
},
|
|
.probe = pwm_probe,
|
|
.probe = pwm_probe,
|
|
.remove = pwm_remove,
|
|
.remove = pwm_remove,
|