diff options
Diffstat (limited to 'greybus_pwm.patch')
| -rw-r--r-- | greybus_pwm.patch | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/greybus_pwm.patch b/greybus_pwm.patch new file mode 100644 index 00000000000000..3f6ad0059d2052 --- /dev/null +++ b/greybus_pwm.patch @@ -0,0 +1,345 @@ +--- + drivers/greybus/pwm.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 338 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/pwm.c +@@ -0,0 +1,338 @@ ++/* ++ * PWM Greybus driver. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/pwm.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++struct gb_pwm_chip { ++ struct gb_connection *connection; ++ u8 pwm_max; /* max pwm number */ ++ ++ struct pwm_chip chip; ++ struct pwm_chip *pwm; ++}; ++#define pwm_chip_to_gb_pwm_chip(chip) \ ++ container_of(chip, struct gb_pwm_chip, chip) ++ ++ ++static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) ++{ ++ struct gb_pwm_count_response response; ++ int ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, ++ NULL, 0, &response, sizeof(response)); ++ if (ret) ++ return ret; ++ pwmc->pwm_max = response.count; ++ return 0; ++} ++ ++static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_activate_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_deactivate_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, ++ u8 which, u32 duty, u32 period) ++{ ++ struct gb_pwm_config_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ request.duty = cpu_to_le32(duty); ++ request.period = cpu_to_le32(period); ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, ++ u8 which, u8 polarity) ++{ ++ struct gb_pwm_polarity_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ request.polarity = polarity; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_enable_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_disable_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_activate_operation(pwmc, pwm->hwpwm); ++}; ++ ++static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ if (pwm_is_enabled(pwm)) ++ dev_warn(chip->dev, "freeing PWM device without disabling\n"); ++ ++ gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); ++} ++ ++static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); ++}; ++ ++static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, ++ enum pwm_polarity polarity) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); ++}; ++ ++static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_enable_operation(pwmc, pwm->hwpwm); ++}; ++ ++static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ gb_pwm_disable_operation(pwmc, pwm->hwpwm); ++}; ++ ++static const struct pwm_ops gb_pwm_ops = { ++ .request = gb_pwm_request, ++ .free = gb_pwm_free, ++ .config = gb_pwm_config, ++ .set_polarity = gb_pwm_set_polarity, ++ .enable = gb_pwm_enable, ++ .disable = gb_pwm_disable, ++ .owner = THIS_MODULE, ++}; ++ ++static int gb_pwm_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct gb_pwm_chip *pwmc; ++ struct pwm_chip *pwm; ++ int ret; ++ ++ pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); ++ if (!pwmc) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto exit_pwmc_free; ++ } ++ ++ pwmc->connection = connection; ++ gb_connection_set_data(connection, pwmc); ++ gb_gbphy_set_data(gbphy_dev, pwmc); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ /* Query number of pwms present */ ++ ret = gb_pwm_count_operation(pwmc); ++ if (ret) ++ goto exit_connection_disable; ++ ++ pwm = &pwmc->chip; ++ ++ pwm->dev = &gbphy_dev->dev; ++ pwm->ops = &gb_pwm_ops; ++ pwm->base = -1; /* Allocate base dynamically */ ++ pwm->npwm = pwmc->pwm_max + 1; ++ pwm->can_sleep = true; /* FIXME */ ++ ++ ret = pwmchip_add(pwm); ++ if (ret) { ++ dev_err(&gbphy_dev->dev, ++ "failed to register PWM: %d\n", ret); ++ goto exit_connection_disable; ++ } ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_pwmc_free: ++ kfree(pwmc); ++ return ret; ++} ++ ++static void gb_pwm_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_pwm_chip *pwmc = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = pwmc->connection; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ pwmchip_remove(&pwmc->chip); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ kfree(pwmc); ++} ++ ++static const struct gbphy_device_id gb_pwm_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_pwm_id_table); ++ ++static struct gbphy_driver pwm_driver = { ++ .name = "pwm", ++ .probe = gb_pwm_probe, ++ .remove = gb_pwm_remove, ++ .id_table = gb_pwm_id_table, ++}; ++ ++module_gbphy_driver(pwm_driver); ++MODULE_LICENSE("GPL v2"); |
