14964cb52SEnric Balletbo i Serra // SPDX-License-Identifier: GPL-2.0
21f0d3bb0SBrian Norris /*
31f0d3bb0SBrian Norris * Expose a PWM controlled by the ChromeOS EC to the host processor.
44964cb52SEnric Balletbo i Serra *
54964cb52SEnric Balletbo i Serra * Copyright (C) 2016 Google, Inc.
61f0d3bb0SBrian Norris */
71f0d3bb0SBrian Norris
81f0d3bb0SBrian Norris #include <linux/module.h>
9*0a41b0c5SRob Herring #include <linux/of.h>
10840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h>
11840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h>
121f0d3bb0SBrian Norris #include <linux/platform_device.h>
131f0d3bb0SBrian Norris #include <linux/pwm.h>
141f0d3bb0SBrian Norris #include <linux/slab.h>
151f0d3bb0SBrian Norris
163d593b6eSFabio Baltieri #include <dt-bindings/mfd/cros_ec.h>
173d593b6eSFabio Baltieri
181f0d3bb0SBrian Norris /**
191f0d3bb0SBrian Norris * struct cros_ec_pwm_device - Driver data for EC PWM
201f0d3bb0SBrian Norris *
211f0d3bb0SBrian Norris * @dev: Device node
221f0d3bb0SBrian Norris * @ec: Pointer to EC device
231f0d3bb0SBrian Norris * @chip: PWM controller chip
243d593b6eSFabio Baltieri * @use_pwm_type: Use PWM types instead of generic channels
251f0d3bb0SBrian Norris */
261f0d3bb0SBrian Norris struct cros_ec_pwm_device {
271f0d3bb0SBrian Norris struct device *dev;
281f0d3bb0SBrian Norris struct cros_ec_device *ec;
291f0d3bb0SBrian Norris struct pwm_chip chip;
303d593b6eSFabio Baltieri bool use_pwm_type;
311f0d3bb0SBrian Norris };
321f0d3bb0SBrian Norris
331db37f95SThierry Reding /**
341db37f95SThierry Reding * struct cros_ec_pwm - per-PWM driver data
351db37f95SThierry Reding * @duty_cycle: cached duty cycle
361db37f95SThierry Reding */
371db37f95SThierry Reding struct cros_ec_pwm {
381db37f95SThierry Reding u16 duty_cycle;
391db37f95SThierry Reding };
401db37f95SThierry Reding
pwm_to_cros_ec_pwm(struct pwm_chip * chip)415996cdf1SUwe Kleine-König static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip)
421f0d3bb0SBrian Norris {
435996cdf1SUwe Kleine-König return container_of(chip, struct cros_ec_pwm_device, chip);
441f0d3bb0SBrian Norris }
451f0d3bb0SBrian Norris
cros_ec_pwm_request(struct pwm_chip * chip,struct pwm_device * pwm)461db37f95SThierry Reding static int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
471db37f95SThierry Reding {
481db37f95SThierry Reding struct cros_ec_pwm *channel;
491db37f95SThierry Reding
501db37f95SThierry Reding channel = kzalloc(sizeof(*channel), GFP_KERNEL);
511db37f95SThierry Reding if (!channel)
521db37f95SThierry Reding return -ENOMEM;
531db37f95SThierry Reding
541db37f95SThierry Reding pwm_set_chip_data(pwm, channel);
551db37f95SThierry Reding
561db37f95SThierry Reding return 0;
571db37f95SThierry Reding }
581db37f95SThierry Reding
cros_ec_pwm_free(struct pwm_chip * chip,struct pwm_device * pwm)591db37f95SThierry Reding static void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
601db37f95SThierry Reding {
611db37f95SThierry Reding struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
621db37f95SThierry Reding
631db37f95SThierry Reding kfree(channel);
641db37f95SThierry Reding }
651db37f95SThierry Reding
cros_ec_dt_type_to_pwm_type(u8 dt_index,u8 * pwm_type)663d593b6eSFabio Baltieri static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type)
671f0d3bb0SBrian Norris {
683d593b6eSFabio Baltieri switch (dt_index) {
693d593b6eSFabio Baltieri case CROS_EC_PWM_DT_KB_LIGHT:
703d593b6eSFabio Baltieri *pwm_type = EC_PWM_TYPE_KB_LIGHT;
713d593b6eSFabio Baltieri return 0;
723d593b6eSFabio Baltieri case CROS_EC_PWM_DT_DISPLAY_LIGHT:
733d593b6eSFabio Baltieri *pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT;
743d593b6eSFabio Baltieri return 0;
753d593b6eSFabio Baltieri default:
763d593b6eSFabio Baltieri return -EINVAL;
773d593b6eSFabio Baltieri }
783d593b6eSFabio Baltieri }
793d593b6eSFabio Baltieri
cros_ec_pwm_set_duty(struct cros_ec_pwm_device * ec_pwm,u8 index,u16 duty)803d593b6eSFabio Baltieri static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index,
813d593b6eSFabio Baltieri u16 duty)
823d593b6eSFabio Baltieri {
833d593b6eSFabio Baltieri struct cros_ec_device *ec = ec_pwm->ec;
841f0d3bb0SBrian Norris struct {
851f0d3bb0SBrian Norris struct cros_ec_command msg;
861f0d3bb0SBrian Norris struct ec_params_pwm_set_duty params;
87065cfbbbSBrian Norris } __packed buf;
881f0d3bb0SBrian Norris struct ec_params_pwm_set_duty *params = &buf.params;
891f0d3bb0SBrian Norris struct cros_ec_command *msg = &buf.msg;
903d593b6eSFabio Baltieri int ret;
911f0d3bb0SBrian Norris
921f0d3bb0SBrian Norris memset(&buf, 0, sizeof(buf));
931f0d3bb0SBrian Norris
941f0d3bb0SBrian Norris msg->version = 0;
951f0d3bb0SBrian Norris msg->command = EC_CMD_PWM_SET_DUTY;
961f0d3bb0SBrian Norris msg->insize = 0;
971f0d3bb0SBrian Norris msg->outsize = sizeof(*params);
981f0d3bb0SBrian Norris
991f0d3bb0SBrian Norris params->duty = duty;
1003d593b6eSFabio Baltieri
1013d593b6eSFabio Baltieri if (ec_pwm->use_pwm_type) {
1023d593b6eSFabio Baltieri ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);
1033d593b6eSFabio Baltieri if (ret) {
1043d593b6eSFabio Baltieri dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
1053d593b6eSFabio Baltieri return ret;
1063d593b6eSFabio Baltieri }
1073d593b6eSFabio Baltieri params->index = 0;
1083d593b6eSFabio Baltieri } else {
1091f0d3bb0SBrian Norris params->pwm_type = EC_PWM_TYPE_GENERIC;
1101f0d3bb0SBrian Norris params->index = index;
1113d593b6eSFabio Baltieri }
1121f0d3bb0SBrian Norris
1131f0d3bb0SBrian Norris return cros_ec_cmd_xfer_status(ec, msg);
1141f0d3bb0SBrian Norris }
1151f0d3bb0SBrian Norris
cros_ec_pwm_get_duty(struct cros_ec_pwm_device * ec_pwm,u8 index)1163d593b6eSFabio Baltieri static int cros_ec_pwm_get_duty(struct cros_ec_pwm_device *ec_pwm, u8 index)
1171f0d3bb0SBrian Norris {
1183d593b6eSFabio Baltieri struct cros_ec_device *ec = ec_pwm->ec;
1191f0d3bb0SBrian Norris struct {
1201f0d3bb0SBrian Norris struct cros_ec_command msg;
1211f0d3bb0SBrian Norris union {
1221f0d3bb0SBrian Norris struct ec_params_pwm_get_duty params;
1231f0d3bb0SBrian Norris struct ec_response_pwm_get_duty resp;
1241f0d3bb0SBrian Norris };
125065cfbbbSBrian Norris } __packed buf;
1261f0d3bb0SBrian Norris struct ec_params_pwm_get_duty *params = &buf.params;
1271f0d3bb0SBrian Norris struct ec_response_pwm_get_duty *resp = &buf.resp;
1281f0d3bb0SBrian Norris struct cros_ec_command *msg = &buf.msg;
1291f0d3bb0SBrian Norris int ret;
1301f0d3bb0SBrian Norris
1311f0d3bb0SBrian Norris memset(&buf, 0, sizeof(buf));
1321f0d3bb0SBrian Norris
1331f0d3bb0SBrian Norris msg->version = 0;
1341f0d3bb0SBrian Norris msg->command = EC_CMD_PWM_GET_DUTY;
135e47866a1SNick Vaccaro msg->insize = sizeof(*resp);
136e47866a1SNick Vaccaro msg->outsize = sizeof(*params);
1371f0d3bb0SBrian Norris
1383d593b6eSFabio Baltieri if (ec_pwm->use_pwm_type) {
1393d593b6eSFabio Baltieri ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);
1403d593b6eSFabio Baltieri if (ret) {
1413d593b6eSFabio Baltieri dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
1423d593b6eSFabio Baltieri return ret;
1433d593b6eSFabio Baltieri }
1443d593b6eSFabio Baltieri params->index = 0;
1453d593b6eSFabio Baltieri } else {
1461f0d3bb0SBrian Norris params->pwm_type = EC_PWM_TYPE_GENERIC;
1471f0d3bb0SBrian Norris params->index = index;
1483d593b6eSFabio Baltieri }
1491f0d3bb0SBrian Norris
1501f0d3bb0SBrian Norris ret = cros_ec_cmd_xfer_status(ec, msg);
1511f0d3bb0SBrian Norris if (ret < 0)
1521f0d3bb0SBrian Norris return ret;
1531f0d3bb0SBrian Norris
1541f0d3bb0SBrian Norris return resp->duty;
1551f0d3bb0SBrian Norris }
1561f0d3bb0SBrian Norris
cros_ec_pwm_apply(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)1571f0d3bb0SBrian Norris static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
15871523d18SUwe Kleine-König const struct pwm_state *state)
1591f0d3bb0SBrian Norris {
1601f0d3bb0SBrian Norris struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
1611db37f95SThierry Reding struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
1621db37f95SThierry Reding u16 duty_cycle;
1631db37f95SThierry Reding int ret;
1641f0d3bb0SBrian Norris
1651f0d3bb0SBrian Norris /* The EC won't let us change the period */
1661f0d3bb0SBrian Norris if (state->period != EC_PWM_MAX_DUTY)
1671f0d3bb0SBrian Norris return -EINVAL;
1681f0d3bb0SBrian Norris
1699f0f6107SUwe Kleine-König if (state->polarity != PWM_POLARITY_NORMAL)
1709f0f6107SUwe Kleine-König return -EINVAL;
1719f0f6107SUwe Kleine-König
1721f0d3bb0SBrian Norris /*
1731f0d3bb0SBrian Norris * EC doesn't separate the concept of duty cycle and enabled, but
1741f0d3bb0SBrian Norris * kernel does. Translate.
1751f0d3bb0SBrian Norris */
1761f0d3bb0SBrian Norris duty_cycle = state->enabled ? state->duty_cycle : 0;
1771f0d3bb0SBrian Norris
1783d593b6eSFabio Baltieri ret = cros_ec_pwm_set_duty(ec_pwm, pwm->hwpwm, duty_cycle);
1791db37f95SThierry Reding if (ret < 0)
1801db37f95SThierry Reding return ret;
1811db37f95SThierry Reding
1821db37f95SThierry Reding channel->duty_cycle = state->duty_cycle;
1831db37f95SThierry Reding
1841db37f95SThierry Reding return 0;
1851f0d3bb0SBrian Norris }
1861f0d3bb0SBrian Norris
cros_ec_pwm_get_state(struct pwm_chip * chip,struct pwm_device * pwm,struct pwm_state * state)1876c452cffSUwe Kleine-König static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
1881f0d3bb0SBrian Norris struct pwm_state *state)
1891f0d3bb0SBrian Norris {
1901f0d3bb0SBrian Norris struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
1911db37f95SThierry Reding struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
1921f0d3bb0SBrian Norris int ret;
1931f0d3bb0SBrian Norris
1943d593b6eSFabio Baltieri ret = cros_ec_pwm_get_duty(ec_pwm, pwm->hwpwm);
1951f0d3bb0SBrian Norris if (ret < 0) {
1961f0d3bb0SBrian Norris dev_err(chip->dev, "error getting initial duty: %d\n", ret);
197ee02c1cbSUwe Kleine-König return ret;
1981f0d3bb0SBrian Norris }
1991f0d3bb0SBrian Norris
2001f0d3bb0SBrian Norris state->enabled = (ret > 0);
2011f0d3bb0SBrian Norris state->period = EC_PWM_MAX_DUTY;
20230006b77SUwe Kleine-König state->polarity = PWM_POLARITY_NORMAL;
2031f0d3bb0SBrian Norris
2041db37f95SThierry Reding /*
2051db37f95SThierry Reding * Note that "disabled" and "duty cycle == 0" are treated the same. If
2061db37f95SThierry Reding * the cached duty cycle is not zero, used the cached duty cycle. This
2071db37f95SThierry Reding * ensures that the configured duty cycle is kept across a disable and
2081db37f95SThierry Reding * enable operation and avoids potentially confusing consumers.
2091db37f95SThierry Reding *
2101db37f95SThierry Reding * For the case of the initial hardware readout, channel->duty_cycle
2111db37f95SThierry Reding * will be 0 and the actual duty cycle read from the EC is used.
2121db37f95SThierry Reding */
2131db37f95SThierry Reding if (ret == 0 && channel->duty_cycle > 0)
2141db37f95SThierry Reding state->duty_cycle = channel->duty_cycle;
2151db37f95SThierry Reding else
2161f0d3bb0SBrian Norris state->duty_cycle = ret;
2176c452cffSUwe Kleine-König
2186c452cffSUwe Kleine-König return 0;
2191f0d3bb0SBrian Norris }
2201f0d3bb0SBrian Norris
2211f0d3bb0SBrian Norris static struct pwm_device *
cros_ec_pwm_xlate(struct pwm_chip * chip,const struct of_phandle_args * args)2225996cdf1SUwe Kleine-König cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
2231f0d3bb0SBrian Norris {
2241f0d3bb0SBrian Norris struct pwm_device *pwm;
2251f0d3bb0SBrian Norris
2265996cdf1SUwe Kleine-König if (args->args[0] >= chip->npwm)
2271f0d3bb0SBrian Norris return ERR_PTR(-EINVAL);
2281f0d3bb0SBrian Norris
2295996cdf1SUwe Kleine-König pwm = pwm_request_from_chip(chip, args->args[0], NULL);
2301f0d3bb0SBrian Norris if (IS_ERR(pwm))
2311f0d3bb0SBrian Norris return pwm;
2321f0d3bb0SBrian Norris
2331f0d3bb0SBrian Norris /* The EC won't let us change the period */
2341f0d3bb0SBrian Norris pwm->args.period = EC_PWM_MAX_DUTY;
2351f0d3bb0SBrian Norris
2361f0d3bb0SBrian Norris return pwm;
2371f0d3bb0SBrian Norris }
2381f0d3bb0SBrian Norris
2391f0d3bb0SBrian Norris static const struct pwm_ops cros_ec_pwm_ops = {
2401db37f95SThierry Reding .request = cros_ec_pwm_request,
2411db37f95SThierry Reding .free = cros_ec_pwm_free,
2421f0d3bb0SBrian Norris .get_state = cros_ec_pwm_get_state,
2431f0d3bb0SBrian Norris .apply = cros_ec_pwm_apply,
2441f0d3bb0SBrian Norris .owner = THIS_MODULE,
2451f0d3bb0SBrian Norris };
2461f0d3bb0SBrian Norris
247d509f8a7SGuenter Roeck /*
248d509f8a7SGuenter Roeck * Determine the number of supported PWMs. The EC does not return the number
249d509f8a7SGuenter Roeck * of PWMs it supports directly, so we have to read the pwm duty cycle for
250d509f8a7SGuenter Roeck * subsequent channels until we get an error.
251d509f8a7SGuenter Roeck */
cros_ec_num_pwms(struct cros_ec_pwm_device * ec_pwm)2523d593b6eSFabio Baltieri static int cros_ec_num_pwms(struct cros_ec_pwm_device *ec_pwm)
2531f0d3bb0SBrian Norris {
2541f0d3bb0SBrian Norris int i, ret;
2551f0d3bb0SBrian Norris
2561f0d3bb0SBrian Norris /* The index field is only 8 bits */
2571f0d3bb0SBrian Norris for (i = 0; i <= U8_MAX; i++) {
2583d593b6eSFabio Baltieri ret = cros_ec_pwm_get_duty(ec_pwm, i);
2591f0d3bb0SBrian Norris /*
2601f0d3bb0SBrian Norris * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM
2611f0d3bb0SBrian Norris * responses; everything else is treated as an error.
262be020f0dSGuenter Roeck * The EC error codes map to -EOPNOTSUPP and -EINVAL,
263be020f0dSGuenter Roeck * so check for those.
2641f0d3bb0SBrian Norris */
265d509f8a7SGuenter Roeck switch (ret) {
266d509f8a7SGuenter Roeck case -EOPNOTSUPP: /* invalid command */
267d509f8a7SGuenter Roeck return -ENODEV;
268d509f8a7SGuenter Roeck case -EINVAL: /* invalid parameter */
269d509f8a7SGuenter Roeck return i;
270d509f8a7SGuenter Roeck default:
271d509f8a7SGuenter Roeck if (ret < 0)
272d509f8a7SGuenter Roeck return ret;
273d509f8a7SGuenter Roeck break;
274d509f8a7SGuenter Roeck }
2751f0d3bb0SBrian Norris }
2761f0d3bb0SBrian Norris
2771f0d3bb0SBrian Norris return U8_MAX;
2781f0d3bb0SBrian Norris }
2791f0d3bb0SBrian Norris
cros_ec_pwm_probe(struct platform_device * pdev)2801f0d3bb0SBrian Norris static int cros_ec_pwm_probe(struct platform_device *pdev)
2811f0d3bb0SBrian Norris {
2821f0d3bb0SBrian Norris struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
2831f0d3bb0SBrian Norris struct device *dev = &pdev->dev;
2843d593b6eSFabio Baltieri struct device_node *np = pdev->dev.of_node;
2851f0d3bb0SBrian Norris struct cros_ec_pwm_device *ec_pwm;
2861f0d3bb0SBrian Norris struct pwm_chip *chip;
2871f0d3bb0SBrian Norris int ret;
2881f0d3bb0SBrian Norris
2891f0d3bb0SBrian Norris if (!ec) {
2901f0d3bb0SBrian Norris dev_err(dev, "no parent EC device\n");
2911f0d3bb0SBrian Norris return -EINVAL;
2921f0d3bb0SBrian Norris }
2931f0d3bb0SBrian Norris
2941f0d3bb0SBrian Norris ec_pwm = devm_kzalloc(dev, sizeof(*ec_pwm), GFP_KERNEL);
2951f0d3bb0SBrian Norris if (!ec_pwm)
2961f0d3bb0SBrian Norris return -ENOMEM;
2971f0d3bb0SBrian Norris chip = &ec_pwm->chip;
2981f0d3bb0SBrian Norris ec_pwm->ec = ec;
2991f0d3bb0SBrian Norris
3003d593b6eSFabio Baltieri if (of_device_is_compatible(np, "google,cros-ec-pwm-type"))
3013d593b6eSFabio Baltieri ec_pwm->use_pwm_type = true;
3023d593b6eSFabio Baltieri
3031f0d3bb0SBrian Norris /* PWM chip */
3041f0d3bb0SBrian Norris chip->dev = dev;
3051f0d3bb0SBrian Norris chip->ops = &cros_ec_pwm_ops;
3061f0d3bb0SBrian Norris chip->of_xlate = cros_ec_pwm_xlate;
3071f0d3bb0SBrian Norris chip->of_pwm_n_cells = 1;
3083d593b6eSFabio Baltieri
3093d593b6eSFabio Baltieri if (ec_pwm->use_pwm_type) {
3103d593b6eSFabio Baltieri chip->npwm = CROS_EC_PWM_DT_COUNT;
3113d593b6eSFabio Baltieri } else {
3123d593b6eSFabio Baltieri ret = cros_ec_num_pwms(ec_pwm);
3131f0d3bb0SBrian Norris if (ret < 0) {
3141f0d3bb0SBrian Norris dev_err(dev, "Couldn't find PWMs: %d\n", ret);
3151f0d3bb0SBrian Norris return ret;
3161f0d3bb0SBrian Norris }
3171f0d3bb0SBrian Norris chip->npwm = ret;
3183d593b6eSFabio Baltieri }
3193d593b6eSFabio Baltieri
3201f0d3bb0SBrian Norris dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
3211f0d3bb0SBrian Norris
3221f0d3bb0SBrian Norris ret = pwmchip_add(chip);
3231f0d3bb0SBrian Norris if (ret < 0) {
3241f0d3bb0SBrian Norris dev_err(dev, "cannot register PWM: %d\n", ret);
3251f0d3bb0SBrian Norris return ret;
3261f0d3bb0SBrian Norris }
3271f0d3bb0SBrian Norris
3281f0d3bb0SBrian Norris platform_set_drvdata(pdev, ec_pwm);
3291f0d3bb0SBrian Norris
3301f0d3bb0SBrian Norris return ret;
3311f0d3bb0SBrian Norris }
3321f0d3bb0SBrian Norris
cros_ec_pwm_remove(struct platform_device * dev)333159a61a7SUwe Kleine-König static void cros_ec_pwm_remove(struct platform_device *dev)
3341f0d3bb0SBrian Norris {
3351f0d3bb0SBrian Norris struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(dev);
3361f0d3bb0SBrian Norris struct pwm_chip *chip = &ec_pwm->chip;
3371f0d3bb0SBrian Norris
338a08be127SUwe Kleine-König pwmchip_remove(chip);
3391f0d3bb0SBrian Norris }
3401f0d3bb0SBrian Norris
3411f0d3bb0SBrian Norris #ifdef CONFIG_OF
3421f0d3bb0SBrian Norris static const struct of_device_id cros_ec_pwm_of_match[] = {
3431f0d3bb0SBrian Norris { .compatible = "google,cros-ec-pwm" },
3443d593b6eSFabio Baltieri { .compatible = "google,cros-ec-pwm-type" },
3451f0d3bb0SBrian Norris {},
3461f0d3bb0SBrian Norris };
3471f0d3bb0SBrian Norris MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);
3481f0d3bb0SBrian Norris #endif
3491f0d3bb0SBrian Norris
3501f0d3bb0SBrian Norris static struct platform_driver cros_ec_pwm_driver = {
3511f0d3bb0SBrian Norris .probe = cros_ec_pwm_probe,
352159a61a7SUwe Kleine-König .remove_new = cros_ec_pwm_remove,
3531f0d3bb0SBrian Norris .driver = {
3541f0d3bb0SBrian Norris .name = "cros-ec-pwm",
3551f0d3bb0SBrian Norris .of_match_table = of_match_ptr(cros_ec_pwm_of_match),
3561f0d3bb0SBrian Norris },
3571f0d3bb0SBrian Norris };
3581f0d3bb0SBrian Norris module_platform_driver(cros_ec_pwm_driver);
3591f0d3bb0SBrian Norris
3601f0d3bb0SBrian Norris MODULE_ALIAS("platform:cros-ec-pwm");
3611f0d3bb0SBrian Norris MODULE_DESCRIPTION("ChromeOS EC PWM driver");
3621f0d3bb0SBrian Norris MODULE_LICENSE("GPL v2");
363