289 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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/of.h>10#include <linux/platform_data/cros_ec_commands.h>11#include <linux/platform_data/cros_ec_proto.h>12#include <linux/platform_device.h>13#include <linux/pwm.h>14#include <linux/slab.h>15 16#include <dt-bindings/mfd/cros_ec.h>17 18/**19 * struct cros_ec_pwm_device - Driver data for EC PWM20 *21 * @ec: Pointer to EC device22 * @use_pwm_type: Use PWM types instead of generic channels23 */24struct cros_ec_pwm_device {25 struct cros_ec_device *ec;26 bool use_pwm_type;27};28 29static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip)30{31 return pwmchip_get_drvdata(chip);32}33 34static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type)35{36 switch (dt_index) {37 case CROS_EC_PWM_DT_KB_LIGHT:38 *pwm_type = EC_PWM_TYPE_KB_LIGHT;39 return 0;40 case CROS_EC_PWM_DT_DISPLAY_LIGHT:41 *pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT;42 return 0;43 default:44 return -EINVAL;45 }46}47 48static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index,49 u16 duty)50{51 struct cros_ec_device *ec = ec_pwm->ec;52 struct {53 struct cros_ec_command msg;54 struct ec_params_pwm_set_duty params;55 } __packed buf;56 struct ec_params_pwm_set_duty *params = &buf.params;57 struct cros_ec_command *msg = &buf.msg;58 int ret;59 60 memset(&buf, 0, sizeof(buf));61 62 msg->version = 0;63 msg->command = EC_CMD_PWM_SET_DUTY;64 msg->insize = 0;65 msg->outsize = sizeof(*params);66 67 params->duty = duty;68 69 if (ec_pwm->use_pwm_type) {70 ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);71 if (ret) {72 dev_err(ec->dev, "Invalid PWM type index: %d\n", index);73 return ret;74 }75 params->index = 0;76 } else {77 params->pwm_type = EC_PWM_TYPE_GENERIC;78 params->index = index;79 }80 81 return cros_ec_cmd_xfer_status(ec, msg);82}83 84static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, bool use_pwm_type, u8 index)85{86 struct {87 struct cros_ec_command msg;88 union {89 struct ec_params_pwm_get_duty params;90 struct ec_response_pwm_get_duty resp;91 };92 } __packed buf;93 struct ec_params_pwm_get_duty *params = &buf.params;94 struct ec_response_pwm_get_duty *resp = &buf.resp;95 struct cros_ec_command *msg = &buf.msg;96 int ret;97 98 memset(&buf, 0, sizeof(buf));99 100 msg->version = 0;101 msg->command = EC_CMD_PWM_GET_DUTY;102 msg->insize = sizeof(*resp);103 msg->outsize = sizeof(*params);104 105 if (use_pwm_type) {106 ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);107 if (ret) {108 dev_err(ec->dev, "Invalid PWM type index: %d\n", index);109 return ret;110 }111 params->index = 0;112 } else {113 params->pwm_type = EC_PWM_TYPE_GENERIC;114 params->index = index;115 }116 117 ret = cros_ec_cmd_xfer_status(ec, msg);118 if (ret < 0)119 return ret;120 121 return resp->duty;122}123 124static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,125 const struct pwm_state *state)126{127 struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);128 u16 duty_cycle;129 int ret;130 131 /* The EC won't let us change the period */132 if (state->period != EC_PWM_MAX_DUTY)133 return -EINVAL;134 135 if (state->polarity != PWM_POLARITY_NORMAL)136 return -EINVAL;137 138 /*139 * EC doesn't separate the concept of duty cycle and enabled, but140 * kernel does. Translate.141 */142 duty_cycle = state->enabled ? state->duty_cycle : 0;143 144 ret = cros_ec_pwm_set_duty(ec_pwm, pwm->hwpwm, duty_cycle);145 if (ret < 0)146 return ret;147 148 return 0;149}150 151static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,152 struct pwm_state *state)153{154 struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);155 int ret;156 157 ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm);158 if (ret < 0) {159 dev_err(pwmchip_parent(chip), "error getting initial duty: %d\n", ret);160 return ret;161 }162 163 state->enabled = (ret > 0);164 state->duty_cycle = ret;165 state->period = EC_PWM_MAX_DUTY;166 state->polarity = PWM_POLARITY_NORMAL;167 168 return 0;169}170 171static const struct pwm_ops cros_ec_pwm_ops = {172 .get_state = cros_ec_pwm_get_state,173 .apply = cros_ec_pwm_apply,174};175 176/*177 * Determine the number of supported PWMs. The EC does not return the number178 * of PWMs it supports directly, so we have to read the pwm duty cycle for179 * subsequent channels until we get an error.180 */181static int cros_ec_num_pwms(struct cros_ec_device *ec)182{183 int i, ret;184 185 /* The index field is only 8 bits */186 for (i = 0; i <= U8_MAX; i++) {187 /*188 * Note that this function is only called when use_pwm_type is189 * false. With use_pwm_type == true the number of PWMs is fixed.190 */191 ret = cros_ec_pwm_get_duty(ec, false, i);192 /*193 * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM194 * responses; everything else is treated as an error.195 * The EC error codes map to -EOPNOTSUPP and -EINVAL,196 * so check for those.197 */198 switch (ret) {199 case -EOPNOTSUPP: /* invalid command */200 return -ENODEV;201 case -EINVAL: /* invalid parameter */202 return i;203 default:204 if (ret < 0)205 return ret;206 break;207 }208 }209 210 return U8_MAX;211}212 213static int cros_ec_pwm_probe(struct platform_device *pdev)214{215 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);216 struct device *dev = &pdev->dev;217 struct device_node *np = pdev->dev.of_node;218 struct cros_ec_pwm_device *ec_pwm;219 struct pwm_chip *chip;220 bool use_pwm_type = false;221 unsigned int i, npwm;222 int ret;223 224 if (!ec)225 return dev_err_probe(dev, -EINVAL, "no parent EC device\n");226 227 if (of_device_is_compatible(np, "google,cros-ec-pwm-type")) {228 use_pwm_type = true;229 npwm = CROS_EC_PWM_DT_COUNT;230 } else {231 ret = cros_ec_num_pwms(ec);232 if (ret < 0)233 return dev_err_probe(dev, ret, "Couldn't find PWMs\n");234 npwm = ret;235 }236 237 chip = devm_pwmchip_alloc(dev, npwm, sizeof(*ec_pwm));238 if (IS_ERR(chip))239 return PTR_ERR(chip);240 241 ec_pwm = pwm_to_cros_ec_pwm(chip);242 ec_pwm->use_pwm_type = use_pwm_type;243 ec_pwm->ec = ec;244 245 /* PWM chip */246 chip->ops = &cros_ec_pwm_ops;247 248 /*249 * The device tree binding for this device is special as it only uses a250 * single cell (for the hwid) and so doesn't provide a default period.251 * This isn't a big problem though as the hardware only supports a252 * single period length, it's just a bit ugly to make this fit into the253 * pwm core abstractions. So initialize the period here, as254 * of_pwm_xlate_with_flags() won't do that for us.255 */256 for (i = 0; i < npwm; ++i)257 chip->pwms[i].args.period = EC_PWM_MAX_DUTY;258 259 dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);260 261 ret = devm_pwmchip_add(dev, chip);262 if (ret < 0)263 return dev_err_probe(dev, ret, "cannot register PWM\n");264 265 return 0;266}267 268#ifdef CONFIG_OF269static const struct of_device_id cros_ec_pwm_of_match[] = {270 { .compatible = "google,cros-ec-pwm" },271 { .compatible = "google,cros-ec-pwm-type" },272 {},273};274MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);275#endif276 277static struct platform_driver cros_ec_pwm_driver = {278 .probe = cros_ec_pwm_probe,279 .driver = {280 .name = "cros-ec-pwm",281 .of_match_table = of_match_ptr(cros_ec_pwm_of_match),282 },283};284module_platform_driver(cros_ec_pwm_driver);285 286MODULE_ALIAS("platform:cros-ec-pwm");287MODULE_DESCRIPTION("ChromeOS EC PWM driver");288MODULE_LICENSE("GPL v2");289