root/drivers/pwm/pwm-cros-ec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pwm_to_cros_ec_pwm
  2. cros_ec_pwm_set_duty
  3. __cros_ec_pwm_get_duty
  4. cros_ec_pwm_get_duty
  5. cros_ec_pwm_apply
  6. cros_ec_pwm_get_state
  7. cros_ec_pwm_xlate
  8. cros_ec_num_pwms
  9. cros_ec_pwm_probe
  10. cros_ec_pwm_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Expose a PWM controlled by the ChromeOS EC to the host processor.
   4  *
   5  * Copyright (C) 2016 Google, Inc.
   6  */
   7 
   8 #include <linux/module.h>
   9 #include <linux/platform_data/cros_ec_commands.h>
  10 #include <linux/platform_data/cros_ec_proto.h>
  11 #include <linux/platform_device.h>
  12 #include <linux/pwm.h>
  13 #include <linux/slab.h>
  14 
  15 /**
  16  * struct cros_ec_pwm_device - Driver data for EC PWM
  17  *
  18  * @dev: Device node
  19  * @ec: Pointer to EC device
  20  * @chip: PWM controller chip
  21  */
  22 struct cros_ec_pwm_device {
  23         struct device *dev;
  24         struct cros_ec_device *ec;
  25         struct pwm_chip chip;
  26 };
  27 
  28 static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c)
  29 {
  30         return container_of(c, struct cros_ec_pwm_device, chip);
  31 }
  32 
  33 static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
  34 {
  35         struct {
  36                 struct cros_ec_command msg;
  37                 struct ec_params_pwm_set_duty params;
  38         } __packed buf;
  39         struct ec_params_pwm_set_duty *params = &buf.params;
  40         struct cros_ec_command *msg = &buf.msg;
  41 
  42         memset(&buf, 0, sizeof(buf));
  43 
  44         msg->version = 0;
  45         msg->command = EC_CMD_PWM_SET_DUTY;
  46         msg->insize = 0;
  47         msg->outsize = sizeof(*params);
  48 
  49         params->duty = duty;
  50         params->pwm_type = EC_PWM_TYPE_GENERIC;
  51         params->index = index;
  52 
  53         return cros_ec_cmd_xfer_status(ec, msg);
  54 }
  55 
  56 static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
  57                                   u32 *result)
  58 {
  59         struct {
  60                 struct cros_ec_command msg;
  61                 union {
  62                         struct ec_params_pwm_get_duty params;
  63                         struct ec_response_pwm_get_duty resp;
  64                 };
  65         } __packed buf;
  66         struct ec_params_pwm_get_duty *params = &buf.params;
  67         struct ec_response_pwm_get_duty *resp = &buf.resp;
  68         struct cros_ec_command *msg = &buf.msg;
  69         int ret;
  70 
  71         memset(&buf, 0, sizeof(buf));
  72 
  73         msg->version = 0;
  74         msg->command = EC_CMD_PWM_GET_DUTY;
  75         msg->insize = sizeof(*resp);
  76         msg->outsize = sizeof(*params);
  77 
  78         params->pwm_type = EC_PWM_TYPE_GENERIC;
  79         params->index = index;
  80 
  81         ret = cros_ec_cmd_xfer_status(ec, msg);
  82         if (result)
  83                 *result = msg->result;
  84         if (ret < 0)
  85                 return ret;
  86 
  87         return resp->duty;
  88 }
  89 
  90 static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index)
  91 {
  92         return __cros_ec_pwm_get_duty(ec, index, NULL);
  93 }
  94 
  95 static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
  96                              const struct pwm_state *state)
  97 {
  98         struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
  99         int duty_cycle;
 100 
 101         /* The EC won't let us change the period */
 102         if (state->period != EC_PWM_MAX_DUTY)
 103                 return -EINVAL;
 104 
 105         /*
 106          * EC doesn't separate the concept of duty cycle and enabled, but
 107          * kernel does. Translate.
 108          */
 109         duty_cycle = state->enabled ? state->duty_cycle : 0;
 110 
 111         return cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle);
 112 }
 113 
 114 static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 115                                   struct pwm_state *state)
 116 {
 117         struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
 118         int ret;
 119 
 120         ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
 121         if (ret < 0) {
 122                 dev_err(chip->dev, "error getting initial duty: %d\n", ret);
 123                 return;
 124         }
 125 
 126         state->enabled = (ret > 0);
 127         state->period = EC_PWM_MAX_DUTY;
 128 
 129         /* Note that "disabled" and "duty cycle == 0" are treated the same */
 130         state->duty_cycle = ret;
 131 }
 132 
 133 static struct pwm_device *
 134 cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 135 {
 136         struct pwm_device *pwm;
 137 
 138         if (args->args[0] >= pc->npwm)
 139                 return ERR_PTR(-EINVAL);
 140 
 141         pwm = pwm_request_from_chip(pc, args->args[0], NULL);
 142         if (IS_ERR(pwm))
 143                 return pwm;
 144 
 145         /* The EC won't let us change the period */
 146         pwm->args.period = EC_PWM_MAX_DUTY;
 147 
 148         return pwm;
 149 }
 150 
 151 static const struct pwm_ops cros_ec_pwm_ops = {
 152         .get_state      = cros_ec_pwm_get_state,
 153         .apply          = cros_ec_pwm_apply,
 154         .owner          = THIS_MODULE,
 155 };
 156 
 157 static int cros_ec_num_pwms(struct cros_ec_device *ec)
 158 {
 159         int i, ret;
 160 
 161         /* The index field is only 8 bits */
 162         for (i = 0; i <= U8_MAX; i++) {
 163                 u32 result = 0;
 164 
 165                 ret = __cros_ec_pwm_get_duty(ec, i, &result);
 166                 /* We want to parse EC protocol errors */
 167                 if (ret < 0 && !(ret == -EPROTO && result))
 168                         return ret;
 169 
 170                 /*
 171                  * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM
 172                  * responses; everything else is treated as an error.
 173                  */
 174                 if (result == EC_RES_INVALID_COMMAND)
 175                         return -ENODEV;
 176                 else if (result == EC_RES_INVALID_PARAM)
 177                         return i;
 178                 else if (result)
 179                         return -EPROTO;
 180         }
 181 
 182         return U8_MAX;
 183 }
 184 
 185 static int cros_ec_pwm_probe(struct platform_device *pdev)
 186 {
 187         struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
 188         struct device *dev = &pdev->dev;
 189         struct cros_ec_pwm_device *ec_pwm;
 190         struct pwm_chip *chip;
 191         int ret;
 192 
 193         if (!ec) {
 194                 dev_err(dev, "no parent EC device\n");
 195                 return -EINVAL;
 196         }
 197 
 198         ec_pwm = devm_kzalloc(dev, sizeof(*ec_pwm), GFP_KERNEL);
 199         if (!ec_pwm)
 200                 return -ENOMEM;
 201         chip = &ec_pwm->chip;
 202         ec_pwm->ec = ec;
 203 
 204         /* PWM chip */
 205         chip->dev = dev;
 206         chip->ops = &cros_ec_pwm_ops;
 207         chip->of_xlate = cros_ec_pwm_xlate;
 208         chip->of_pwm_n_cells = 1;
 209         chip->base = -1;
 210         ret = cros_ec_num_pwms(ec);
 211         if (ret < 0) {
 212                 dev_err(dev, "Couldn't find PWMs: %d\n", ret);
 213                 return ret;
 214         }
 215         chip->npwm = ret;
 216         dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
 217 
 218         ret = pwmchip_add(chip);
 219         if (ret < 0) {
 220                 dev_err(dev, "cannot register PWM: %d\n", ret);
 221                 return ret;
 222         }
 223 
 224         platform_set_drvdata(pdev, ec_pwm);
 225 
 226         return ret;
 227 }
 228 
 229 static int cros_ec_pwm_remove(struct platform_device *dev)
 230 {
 231         struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(dev);
 232         struct pwm_chip *chip = &ec_pwm->chip;
 233 
 234         return pwmchip_remove(chip);
 235 }
 236 
 237 #ifdef CONFIG_OF
 238 static const struct of_device_id cros_ec_pwm_of_match[] = {
 239         { .compatible = "google,cros-ec-pwm" },
 240         {},
 241 };
 242 MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);
 243 #endif
 244 
 245 static struct platform_driver cros_ec_pwm_driver = {
 246         .probe = cros_ec_pwm_probe,
 247         .remove = cros_ec_pwm_remove,
 248         .driver = {
 249                 .name = "cros-ec-pwm",
 250                 .of_match_table = of_match_ptr(cros_ec_pwm_of_match),
 251         },
 252 };
 253 module_platform_driver(cros_ec_pwm_driver);
 254 
 255 MODULE_ALIAS("platform:cros-ec-pwm");
 256 MODULE_DESCRIPTION("ChromeOS EC PWM driver");
 257 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */