Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Azoteq IQS620A PWM Generator
0004  *
0005  * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
0006  *
0007  * Limitations:
0008  * - The period is fixed to 1 ms and is generated continuously despite changes
0009  *   to the duty cycle or enable/disable state.
0010  * - Changes to the duty cycle or enable/disable state take effect immediately
0011  *   and may result in a glitch during the period in which the change is made.
0012  * - The device cannot generate a 0% duty cycle. For duty cycles below 1 / 256
0013  *   ms, the output is disabled and relies upon an external pull-down resistor
0014  *   to hold the GPIO3/LTX pin low.
0015  */
0016 
0017 #include <linux/device.h>
0018 #include <linux/kernel.h>
0019 #include <linux/mfd/iqs62x.h>
0020 #include <linux/module.h>
0021 #include <linux/mutex.h>
0022 #include <linux/notifier.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/pwm.h>
0025 #include <linux/regmap.h>
0026 #include <linux/slab.h>
0027 
0028 #define IQS620_PWR_SETTINGS         0xd2
0029 #define IQS620_PWR_SETTINGS_PWM_OUT     BIT(7)
0030 
0031 #define IQS620_PWM_DUTY_CYCLE           0xd8
0032 
0033 #define IQS620_PWM_PERIOD_NS            1000000
0034 
0035 struct iqs620_pwm_private {
0036     struct iqs62x_core *iqs62x;
0037     struct pwm_chip chip;
0038     struct notifier_block notifier;
0039     struct mutex lock;
0040     unsigned int duty_scale;
0041 };
0042 
0043 static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm,
0044                unsigned int duty_scale)
0045 {
0046     struct iqs62x_core *iqs62x = iqs620_pwm->iqs62x;
0047     int ret;
0048 
0049     if (!duty_scale)
0050         return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
0051                       IQS620_PWR_SETTINGS_PWM_OUT, 0);
0052 
0053     ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
0054                duty_scale - 1);
0055     if (ret)
0056         return ret;
0057 
0058     return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
0059                   IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
0060 }
0061 
0062 static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0063                 const struct pwm_state *state)
0064 {
0065     struct iqs620_pwm_private *iqs620_pwm;
0066     unsigned int duty_cycle;
0067     unsigned int duty_scale;
0068     int ret;
0069 
0070     if (state->polarity != PWM_POLARITY_NORMAL)
0071         return -EINVAL;
0072 
0073     if (state->period < IQS620_PWM_PERIOD_NS)
0074         return -EINVAL;
0075 
0076     iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
0077 
0078     /*
0079      * The duty cycle generated by the device is calculated as follows:
0080      *
0081      * duty_cycle = (IQS620_PWM_DUTY_CYCLE + 1) / 256 * 1 ms
0082      *
0083      * ...where IQS620_PWM_DUTY_CYCLE is a register value between 0 and 255
0084      * (inclusive). Therefore the lowest duty cycle the device can generate
0085      * while the output is enabled is 1 / 256 ms.
0086      *
0087      * For lower duty cycles (e.g. 0), the PWM output is simply disabled to
0088      * allow an external pull-down resistor to hold the GPIO3/LTX pin low.
0089      */
0090     duty_cycle = min_t(u64, state->duty_cycle, IQS620_PWM_PERIOD_NS);
0091     duty_scale = duty_cycle * 256 / IQS620_PWM_PERIOD_NS;
0092 
0093     if (!state->enabled)
0094         duty_scale = 0;
0095 
0096     mutex_lock(&iqs620_pwm->lock);
0097 
0098     ret = iqs620_pwm_init(iqs620_pwm, duty_scale);
0099     if (!ret)
0100         iqs620_pwm->duty_scale = duty_scale;
0101 
0102     mutex_unlock(&iqs620_pwm->lock);
0103 
0104     return ret;
0105 }
0106 
0107 static void iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
0108                  struct pwm_state *state)
0109 {
0110     struct iqs620_pwm_private *iqs620_pwm;
0111 
0112     iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
0113 
0114     mutex_lock(&iqs620_pwm->lock);
0115 
0116     /*
0117      * Since the device cannot generate a 0% duty cycle, requests to do so
0118      * cause subsequent calls to iqs620_pwm_get_state to report the output
0119      * as disabled. This is not ideal, but is the best compromise based on
0120      * the capabilities of the device.
0121      */
0122     state->enabled = iqs620_pwm->duty_scale > 0;
0123     state->duty_cycle = DIV_ROUND_UP(iqs620_pwm->duty_scale *
0124                      IQS620_PWM_PERIOD_NS, 256);
0125 
0126     mutex_unlock(&iqs620_pwm->lock);
0127 
0128     state->period = IQS620_PWM_PERIOD_NS;
0129 }
0130 
0131 static int iqs620_pwm_notifier(struct notifier_block *notifier,
0132                    unsigned long event_flags, void *context)
0133 {
0134     struct iqs620_pwm_private *iqs620_pwm;
0135     int ret;
0136 
0137     if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
0138         return NOTIFY_DONE;
0139 
0140     iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
0141                   notifier);
0142 
0143     mutex_lock(&iqs620_pwm->lock);
0144 
0145     /*
0146      * The parent MFD driver already prints an error message in the event
0147      * of a device reset, so nothing else is printed here unless there is
0148      * an additional failure.
0149      */
0150     ret = iqs620_pwm_init(iqs620_pwm, iqs620_pwm->duty_scale);
0151 
0152     mutex_unlock(&iqs620_pwm->lock);
0153 
0154     if (ret) {
0155         dev_err(iqs620_pwm->chip.dev,
0156             "Failed to re-initialize device: %d\n", ret);
0157         return NOTIFY_BAD;
0158     }
0159 
0160     return NOTIFY_OK;
0161 }
0162 
0163 static const struct pwm_ops iqs620_pwm_ops = {
0164     .apply = iqs620_pwm_apply,
0165     .get_state = iqs620_pwm_get_state,
0166     .owner = THIS_MODULE,
0167 };
0168 
0169 static void iqs620_pwm_notifier_unregister(void *context)
0170 {
0171     struct iqs620_pwm_private *iqs620_pwm = context;
0172     int ret;
0173 
0174     ret = blocking_notifier_chain_unregister(&iqs620_pwm->iqs62x->nh,
0175                          &iqs620_pwm->notifier);
0176     if (ret)
0177         dev_err(iqs620_pwm->chip.dev,
0178             "Failed to unregister notifier: %d\n", ret);
0179 }
0180 
0181 static int iqs620_pwm_probe(struct platform_device *pdev)
0182 {
0183     struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
0184     struct iqs620_pwm_private *iqs620_pwm;
0185     unsigned int val;
0186     int ret;
0187 
0188     iqs620_pwm = devm_kzalloc(&pdev->dev, sizeof(*iqs620_pwm), GFP_KERNEL);
0189     if (!iqs620_pwm)
0190         return -ENOMEM;
0191 
0192     iqs620_pwm->iqs62x = iqs62x;
0193 
0194     ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);
0195     if (ret)
0196         return ret;
0197 
0198     if (val & IQS620_PWR_SETTINGS_PWM_OUT) {
0199         ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
0200         if (ret)
0201             return ret;
0202 
0203         iqs620_pwm->duty_scale = val + 1;
0204     }
0205 
0206     iqs620_pwm->chip.dev = &pdev->dev;
0207     iqs620_pwm->chip.ops = &iqs620_pwm_ops;
0208     iqs620_pwm->chip.npwm = 1;
0209 
0210     mutex_init(&iqs620_pwm->lock);
0211 
0212     iqs620_pwm->notifier.notifier_call = iqs620_pwm_notifier;
0213     ret = blocking_notifier_chain_register(&iqs620_pwm->iqs62x->nh,
0214                            &iqs620_pwm->notifier);
0215     if (ret) {
0216         dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
0217         return ret;
0218     }
0219 
0220     ret = devm_add_action_or_reset(&pdev->dev,
0221                        iqs620_pwm_notifier_unregister,
0222                        iqs620_pwm);
0223     if (ret)
0224         return ret;
0225 
0226     ret = devm_pwmchip_add(&pdev->dev, &iqs620_pwm->chip);
0227     if (ret)
0228         dev_err(&pdev->dev, "Failed to add device: %d\n", ret);
0229 
0230     return ret;
0231 }
0232 
0233 static struct platform_driver iqs620_pwm_platform_driver = {
0234     .driver = {
0235         .name = "iqs620a-pwm",
0236     },
0237     .probe = iqs620_pwm_probe,
0238 };
0239 module_platform_driver(iqs620_pwm_platform_driver);
0240 
0241 MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
0242 MODULE_DESCRIPTION("Azoteq IQS620A PWM Generator");
0243 MODULE_LICENSE("GPL");
0244 MODULE_ALIAS("platform:iqs620a-pwm");