18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Expose a PWM controlled by the ChromeOS EC to the host processor. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Google, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_commands.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_proto.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pwm.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/** 168c2ecf20Sopenharmony_ci * struct cros_ec_pwm_device - Driver data for EC PWM 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * @dev: Device node 198c2ecf20Sopenharmony_ci * @ec: Pointer to EC device 208c2ecf20Sopenharmony_ci * @chip: PWM controller chip 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistruct cros_ec_pwm_device { 238c2ecf20Sopenharmony_ci struct device *dev; 248c2ecf20Sopenharmony_ci struct cros_ec_device *ec; 258c2ecf20Sopenharmony_ci struct pwm_chip chip; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/** 298c2ecf20Sopenharmony_ci * struct cros_ec_pwm - per-PWM driver data 308c2ecf20Sopenharmony_ci * @duty_cycle: cached duty cycle 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistruct cros_ec_pwm { 338c2ecf20Sopenharmony_ci u16 duty_cycle; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return container_of(c, struct cros_ec_pwm_device, chip); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct cros_ec_pwm *channel; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci channel = kzalloc(sizeof(*channel), GFP_KERNEL); 468c2ecf20Sopenharmony_ci if (!channel) 478c2ecf20Sopenharmony_ci return -ENOMEM; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci pwm_set_chip_data(pwm, channel); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci kfree(channel); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct { 648c2ecf20Sopenharmony_ci struct cros_ec_command msg; 658c2ecf20Sopenharmony_ci struct ec_params_pwm_set_duty params; 668c2ecf20Sopenharmony_ci } __packed buf; 678c2ecf20Sopenharmony_ci struct ec_params_pwm_set_duty *params = &buf.params; 688c2ecf20Sopenharmony_ci struct cros_ec_command *msg = &buf.msg; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci msg->version = 0; 738c2ecf20Sopenharmony_ci msg->command = EC_CMD_PWM_SET_DUTY; 748c2ecf20Sopenharmony_ci msg->insize = 0; 758c2ecf20Sopenharmony_ci msg->outsize = sizeof(*params); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci params->duty = duty; 788c2ecf20Sopenharmony_ci params->pwm_type = EC_PWM_TYPE_GENERIC; 798c2ecf20Sopenharmony_ci params->index = index; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return cros_ec_cmd_xfer_status(ec, msg); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct { 878c2ecf20Sopenharmony_ci struct cros_ec_command msg; 888c2ecf20Sopenharmony_ci union { 898c2ecf20Sopenharmony_ci struct ec_params_pwm_get_duty params; 908c2ecf20Sopenharmony_ci struct ec_response_pwm_get_duty resp; 918c2ecf20Sopenharmony_ci }; 928c2ecf20Sopenharmony_ci } __packed buf; 938c2ecf20Sopenharmony_ci struct ec_params_pwm_get_duty *params = &buf.params; 948c2ecf20Sopenharmony_ci struct ec_response_pwm_get_duty *resp = &buf.resp; 958c2ecf20Sopenharmony_ci struct cros_ec_command *msg = &buf.msg; 968c2ecf20Sopenharmony_ci int ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci msg->version = 0; 1018c2ecf20Sopenharmony_ci msg->command = EC_CMD_PWM_GET_DUTY; 1028c2ecf20Sopenharmony_ci msg->insize = sizeof(*resp); 1038c2ecf20Sopenharmony_ci msg->outsize = sizeof(*params); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci params->pwm_type = EC_PWM_TYPE_GENERIC; 1068c2ecf20Sopenharmony_ci params->index = index; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer_status(ec, msg); 1098c2ecf20Sopenharmony_ci if (ret < 0) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return resp->duty; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 1168c2ecf20Sopenharmony_ci const struct pwm_state *state) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); 1198c2ecf20Sopenharmony_ci struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); 1208c2ecf20Sopenharmony_ci u16 duty_cycle; 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* The EC won't let us change the period */ 1248c2ecf20Sopenharmony_ci if (state->period != EC_PWM_MAX_DUTY) 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * EC doesn't separate the concept of duty cycle and enabled, but 1298c2ecf20Sopenharmony_ci * kernel does. Translate. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci duty_cycle = state->enabled ? state->duty_cycle : 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle); 1348c2ecf20Sopenharmony_ci if (ret < 0) 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci channel->duty_cycle = state->duty_cycle; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 1438c2ecf20Sopenharmony_ci struct pwm_state *state) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); 1468c2ecf20Sopenharmony_ci struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm); 1508c2ecf20Sopenharmony_ci if (ret < 0) { 1518c2ecf20Sopenharmony_ci dev_err(chip->dev, "error getting initial duty: %d\n", ret); 1528c2ecf20Sopenharmony_ci return; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci state->enabled = (ret > 0); 1568c2ecf20Sopenharmony_ci state->period = EC_PWM_MAX_DUTY; 1578c2ecf20Sopenharmony_ci state->polarity = PWM_POLARITY_NORMAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Note that "disabled" and "duty cycle == 0" are treated the same. If 1618c2ecf20Sopenharmony_ci * the cached duty cycle is not zero, used the cached duty cycle. This 1628c2ecf20Sopenharmony_ci * ensures that the configured duty cycle is kept across a disable and 1638c2ecf20Sopenharmony_ci * enable operation and avoids potentially confusing consumers. 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * For the case of the initial hardware readout, channel->duty_cycle 1668c2ecf20Sopenharmony_ci * will be 0 and the actual duty cycle read from the EC is used. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci if (ret == 0 && channel->duty_cycle > 0) 1698c2ecf20Sopenharmony_ci state->duty_cycle = channel->duty_cycle; 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci state->duty_cycle = ret; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic struct pwm_device * 1758c2ecf20Sopenharmony_cicros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct pwm_device *pwm; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (args->args[0] >= pc->npwm) 1808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci pwm = pwm_request_from_chip(pc, args->args[0], NULL); 1838c2ecf20Sopenharmony_ci if (IS_ERR(pwm)) 1848c2ecf20Sopenharmony_ci return pwm; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* The EC won't let us change the period */ 1878c2ecf20Sopenharmony_ci pwm->args.period = EC_PWM_MAX_DUTY; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return pwm; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct pwm_ops cros_ec_pwm_ops = { 1938c2ecf20Sopenharmony_ci .request = cros_ec_pwm_request, 1948c2ecf20Sopenharmony_ci .free = cros_ec_pwm_free, 1958c2ecf20Sopenharmony_ci .get_state = cros_ec_pwm_get_state, 1968c2ecf20Sopenharmony_ci .apply = cros_ec_pwm_apply, 1978c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * Determine the number of supported PWMs. The EC does not return the number 2028c2ecf20Sopenharmony_ci * of PWMs it supports directly, so we have to read the pwm duty cycle for 2038c2ecf20Sopenharmony_ci * subsequent channels until we get an error. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic int cros_ec_num_pwms(struct cros_ec_device *ec) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int i, ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* The index field is only 8 bits */ 2108c2ecf20Sopenharmony_ci for (i = 0; i <= U8_MAX; i++) { 2118c2ecf20Sopenharmony_ci ret = cros_ec_pwm_get_duty(ec, i); 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM 2148c2ecf20Sopenharmony_ci * responses; everything else is treated as an error. 2158c2ecf20Sopenharmony_ci * The EC error codes map to -EOPNOTSUPP and -EINVAL, 2168c2ecf20Sopenharmony_ci * so check for those. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci switch (ret) { 2198c2ecf20Sopenharmony_ci case -EOPNOTSUPP: /* invalid command */ 2208c2ecf20Sopenharmony_ci return -ENODEV; 2218c2ecf20Sopenharmony_ci case -EINVAL: /* invalid parameter */ 2228c2ecf20Sopenharmony_ci return i; 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci if (ret < 0) 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return U8_MAX; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int cros_ec_pwm_probe(struct platform_device *pdev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 2368c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2378c2ecf20Sopenharmony_ci struct cros_ec_pwm_device *ec_pwm; 2388c2ecf20Sopenharmony_ci struct pwm_chip *chip; 2398c2ecf20Sopenharmony_ci int ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (!ec) { 2428c2ecf20Sopenharmony_ci dev_err(dev, "no parent EC device\n"); 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ec_pwm = devm_kzalloc(dev, sizeof(*ec_pwm), GFP_KERNEL); 2478c2ecf20Sopenharmony_ci if (!ec_pwm) 2488c2ecf20Sopenharmony_ci return -ENOMEM; 2498c2ecf20Sopenharmony_ci chip = &ec_pwm->chip; 2508c2ecf20Sopenharmony_ci ec_pwm->ec = ec; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* PWM chip */ 2538c2ecf20Sopenharmony_ci chip->dev = dev; 2548c2ecf20Sopenharmony_ci chip->ops = &cros_ec_pwm_ops; 2558c2ecf20Sopenharmony_ci chip->of_xlate = cros_ec_pwm_xlate; 2568c2ecf20Sopenharmony_ci chip->of_pwm_n_cells = 1; 2578c2ecf20Sopenharmony_ci chip->base = -1; 2588c2ecf20Sopenharmony_ci ret = cros_ec_num_pwms(ec); 2598c2ecf20Sopenharmony_ci if (ret < 0) { 2608c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't find PWMs: %d\n", ret); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci chip->npwm = ret; 2648c2ecf20Sopenharmony_ci dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ret = pwmchip_add(chip); 2678c2ecf20Sopenharmony_ci if (ret < 0) { 2688c2ecf20Sopenharmony_ci dev_err(dev, "cannot register PWM: %d\n", ret); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ec_pwm); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int cros_ec_pwm_remove(struct platform_device *dev) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(dev); 2808c2ecf20Sopenharmony_ci struct pwm_chip *chip = &ec_pwm->chip; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return pwmchip_remove(chip); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2868c2ecf20Sopenharmony_cistatic const struct of_device_id cros_ec_pwm_of_match[] = { 2878c2ecf20Sopenharmony_ci { .compatible = "google,cros-ec-pwm" }, 2888c2ecf20Sopenharmony_ci {}, 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match); 2918c2ecf20Sopenharmony_ci#endif 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct platform_driver cros_ec_pwm_driver = { 2948c2ecf20Sopenharmony_ci .probe = cros_ec_pwm_probe, 2958c2ecf20Sopenharmony_ci .remove = cros_ec_pwm_remove, 2968c2ecf20Sopenharmony_ci .driver = { 2978c2ecf20Sopenharmony_ci .name = "cros-ec-pwm", 2988c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(cros_ec_pwm_of_match), 2998c2ecf20Sopenharmony_ci }, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_cimodule_platform_driver(cros_ec_pwm_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:cros-ec-pwm"); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ChromeOS EC PWM driver"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 306