This is a part of the LP3943 MFD driver.
LP3943 can be used as a PWM generator, up to 2 channels.

* Two PWM generators
  LP3943 has 16 output channels.
  Two channels are configurable as PWM generators.

* Supported PWM operations
  config, set_polarity, enable and disable

* Manual polarity configuration
  No register exists for setting polarity. However, the value of duty can be
  calculated as following.
  PWM-inversed mode : duty = period - duty
  PWM-normal mode   : input duty itself

* Pin MUX configuration at each PWM operation - enable and disable

* Register access through exported LP3943 MFD functions

Signed-off-by: Milo Kim <[email protected]>
---
 drivers/pwm/Kconfig      |   10 +++
 drivers/pwm/Makefile     |    1 +
 drivers/pwm/pwm-lp3943.c |  192 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 drivers/pwm/pwm-lp3943.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 75840b5..28a7668 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -81,6 +81,16 @@ config PWM_JZ4740
          To compile this driver as a module, choose M here: the module
          will be called pwm-jz4740.
 
+config PWM_LP3943
+       tristate "LP3943 PWM support"
+       depends on MFD_LP3943
+       help
+         Generic PWM framework driver for LP3943. It supports two PWM
+         controllers.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-lp3943.
+
 config PWM_LPC32XX
        tristate "LPC32XX PWM support"
        depends on ARCH_LPC32XX
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 77a8c18..db8e349 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_PWM_ATMEL_TCB)     += pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BFIN)         += pwm-bfin.o
 obj-$(CONFIG_PWM_IMX)          += pwm-imx.o
 obj-$(CONFIG_PWM_JZ4740)       += pwm-jz4740.o
+obj-$(CONFIG_PWM_LP3943)       += pwm-lp3943.o
 obj-$(CONFIG_PWM_LPC32XX)      += pwm-lpc32xx.o
 obj-$(CONFIG_PWM_MXS)          += pwm-mxs.o
 obj-$(CONFIG_PWM_PCA9685)      += pwm-pca9685.o
diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c
new file mode 100644
index 0000000..33e63fa7
--- /dev/null
+++ b/drivers/pwm/pwm-lp3943.c
@@ -0,0 +1,192 @@
+/*
+ * TI/National Semiconductor LP3943 PWM driver
+ *
+ *                     Copyright (C) 2013 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/lp3943.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define LP3943_MAX_DUTY                        255
+#define LP3943_MIN_PERIOD              6250
+#define LP3943_MAX_PERIOD              1600000
+#define LP3943_NUM_PWMS                        2
+
+#define to_lp3943_pwm(_chip)   container_of(_chip, struct lp3943_pwm, chip)
+
+struct lp3943_pwm {
+       struct pwm_chip chip;
+       struct lp3943 *l;
+       enum lp3943_pwm_output output[LP3943_NUM_PWMS];
+       bool inversed[LP3943_NUM_PWMS];
+};
+
+static int lp3943_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                     int duty_ns, int period_ns)
+{
+       struct lp3943_pwm *lp = to_lp3943_pwm(chip);
+       u8 reg_prescale;
+       u8 reg_duty;
+       u8 val;
+       int err;
+
+       /*
+        * How to configure the LP3943 PWMs
+        *
+        * 1) Period = 6250 ~ 1600000
+        * 2) Prescale = period / 6250 -1
+        * 3) Duty = 'input duty'(normal) or 'period - duty'(inversed)
+        *
+        * Prescale and duty are register values
+        */
+
+       if (pwm->hwpwm == 0) {
+               reg_prescale = LP3943_REG_PRESCALE0;
+               reg_duty = LP3943_REG_PWM0;
+       } else if (pwm->hwpwm == 1) {
+               reg_prescale = LP3943_REG_PRESCALE1;
+               reg_duty = LP3943_REG_PWM1;
+       } else {
+               return -EINVAL;
+       }
+
+       period_ns = clamp(period_ns, LP3943_MIN_PERIOD, LP3943_MAX_PERIOD);
+       val = (u8)(period_ns / LP3943_MIN_PERIOD - 1);
+
+       err = lp3943_write_byte(lp->l, reg_prescale, val);
+       if (err)
+               return err;
+
+       if (lp->inversed[pwm->hwpwm])
+               val = (u8)((period_ns - duty_ns) * LP3943_MAX_DUTY / period_ns);
+       else
+               val = (u8)(duty_ns * LP3943_MAX_DUTY / period_ns);
+
+       return lp3943_write_byte(lp->l, reg_duty, val);
+}
+
+static int lp3943_pwm_set_polarity(struct pwm_chip *chip,
+                       struct pwm_device *pwm, enum pwm_polarity polarity)
+{
+       struct lp3943_pwm *lp = to_lp3943_pwm(chip);
+
+       if (polarity == PWM_POLARITY_INVERSED)
+               lp->inversed[pwm->hwpwm] = true;
+       else
+               lp->inversed[pwm->hwpwm] = false;
+
+       return 0;
+}
+
+static int lp3943_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct lp3943_pwm *lp = to_lp3943_pwm(chip);
+       const struct lp3943_reg_cfg *mux = lp->l->mux_cfg;
+       int offset = lp->output[pwm->hwpwm] - 1;
+       u8 val;
+
+       if (pwm->hwpwm == 0)
+               val = LP3943_DIM_PWM0;
+       else if (pwm->hwpwm == 1)
+               val = LP3943_DIM_PWM1;
+       else
+               return -EINVAL;
+
+       return lp3943_update_bits(lp->l, mux[offset].reg, mux[offset].mask,
+                               val << mux[offset].shift);
+}
+
+static void lp3943_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct lp3943_pwm *lp = to_lp3943_pwm(chip);
+       const struct lp3943_reg_cfg *mux = lp->l->mux_cfg;
+       int offset = lp->output[pwm->hwpwm] - 1;
+       u8 val;
+
+       /*
+        * LP3943 outputs are open-drain, so the pin should be configured
+        * when the PWM is disabled.
+        *
+        * - inversed: output low
+        * - normal: output Hi-Z
+        */
+
+       if (lp->inversed[pwm->hwpwm])
+               val = LP3943_GPIO_OUT_LOW << mux[offset].shift;
+       else
+               val = LP3943_GPIO_OUT_HIGH << mux[offset].shift;
+
+       lp3943_update_bits(lp->l, mux[offset].reg, mux[offset].mask, val);
+}
+
+static const struct pwm_ops lp3943_pwm_ops = {
+       .config  = lp3943_pwm_config,
+       .set_polarity = lp3943_pwm_set_polarity,
+       .enable  = lp3943_pwm_enable,
+       .disable = lp3943_pwm_disable,
+       .owner   = THIS_MODULE,
+};
+
+static int lp3943_pwm_probe(struct platform_device *pdev)
+{
+       struct lp3943 *l = dev_get_drvdata(pdev->dev.parent);
+       struct lp3943_platform_data *pdata = l->pdata;
+       struct lp3943_pwm *lp;
+       int num_pwms = 0;
+       int ret;
+
+       lp = devm_kzalloc(&pdev->dev, sizeof(struct lp3943_pwm), GFP_KERNEL);
+       if (!lp)
+               return -ENOMEM;
+
+       if (pdata->pwm0 != LP3943_PWM_INVALID)
+               lp->output[num_pwms++] = pdata->pwm0;
+
+       if (pdata->pwm1 != LP3943_PWM_INVALID)
+               lp->output[num_pwms++] = pdata->pwm1;
+
+       lp->l = l;
+       lp->chip.dev = &pdev->dev;
+       lp->chip.ops = &lp3943_pwm_ops;
+       lp->chip.npwm = num_pwms;
+
+       platform_set_drvdata(pdev, lp);
+
+       ret = pwmchip_add(&lp->chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to add PWM chip, err:%d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lp3943_pwm_remove(struct platform_device *pdev)
+{
+       struct lp3943_pwm *lp = platform_get_drvdata(pdev);
+       return pwmchip_remove(&lp->chip);
+}
+
+static struct platform_driver lp3943_pwm_driver = {
+       .probe = lp3943_pwm_probe,
+       .remove = lp3943_pwm_remove,
+       .driver = {
+               .name = "lp3943-pwm",
+               .owner = THIS_MODULE,
+       },
+};
+module_platform_driver(lp3943_pwm_driver);
+
+MODULE_DESCRIPTION("LP3943 PWM driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp3943-pwm");
-- 
1.7.9.5


Best Regards,
Milo


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to