blob: 154965a5f06bf4640eeb5328aa8043fe1c651dd9 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020 Google LLC.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include "icr-core.h"
#include <linux/module.h>
#define PULSE_DURATION_MS 100
struct aw8637c_data {
bool flip_polarity;
bool initialized;
bool open_target;
bool open_state;
struct gpio_desc *pwm[2];
struct work_struct pulse_work;
struct device *icr_dev;
};
static void aw8637c_pulse_motor(struct aw8637c_data *data)
{
bool direction = data->flip_polarity ^ data->open_target;
if (direction) {
gpiod_set_value(data->pwm[0], 0);
gpiod_set_value(data->pwm[1], 1);
} else {
gpiod_set_value(data->pwm[0], 1);
gpiod_set_value(data->pwm[1], 0);
}
msleep(PULSE_DURATION_MS);
gpiod_set_value(data->pwm[0], 1);
gpiod_set_value(data->pwm[1], 1);
data->open_state = data->open_target;
data->initialized = 1;
}
static void aw8637c_worker(struct work_struct *work)
{
struct aw8637c_data *data =
container_of(work, struct aw8637c_data, pulse_work);
aw8637c_pulse_motor(data);
}
static ssize_t open_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct aw8637c_data *data = dev_get_drvdata(dev);
if (!data->initialized)
return scnprintf(buf, PAGE_SIZE, "Uninitialized\n");
return scnprintf(buf, PAGE_SIZE, "%d\n", (int)data->open_state);
}
static ssize_t open_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
bool target;
struct aw8637c_data *data = dev_get_drvdata(dev);
ret = kstrtobool(buf, &target);
if (ret < 0)
return ret;
/* Waits for current exection to finish or cancel it if the exection
* has not started.
*/
cancel_work_sync(&data->pulse_work);
data->open_target = target;
schedule_work(&data->pulse_work);
return count;
}
static const struct icr_ops ops = {
.open_show = open_show,
.open_store = open_store,
};
static int aw8637c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aw8637c_data *data =
devm_kmalloc(dev, sizeof(struct aw8637c_data), GFP_KERNEL);
bool skip_init = false;
if (!data)
return -ENOMEM;
data->initialized = 0;
data->flip_polarity = 0;
data->open_target = 0;
data->open_state = 0;
/* Get the GPIO descriptors */
data->pwm[0] = devm_gpiod_get_index(dev, "pwm", 0, GPIOD_OUT_HIGH);
if (IS_ERR(data->pwm[0])) {
dev_err(dev, "Failed to initialize pwm1 gpio pin\n");
return PTR_ERR(data->pwm[0]);
}
data->pwm[1] = devm_gpiod_get_index(dev, "pwm", 1, GPIOD_OUT_HIGH);
if (IS_ERR(data->pwm[1])) {
dev_err(dev, "Failed to initialize pwm2 gpio pin\n");
return PTR_ERR(data->pwm[1]);
}
/* Check whether to skip init */
if (of_find_property(dev->of_node, "skip_init", NULL))
skip_init = 1;
/* Check whether to flip the polarity */
if (of_find_property(dev->of_node, "flip_polarity", NULL))
data->flip_polarity = 1;
/*
* Set the data as platform driver data so that it will be freed when
* remove
*/
platform_set_drvdata(pdev, data);
INIT_WORK(&data->pulse_work, aw8637c_worker);
if (!skip_init) {
dev_info(dev, "Setting AW8367C to default state\n");
schedule_work(&data->pulse_work);
} else {
dev_info(dev, "Skipping setting AW8367C to default state\n");
}
data->icr_dev = icr_core_register(dev, &ops);
if (IS_ERR(data->icr_dev)) {
dev_err(dev, "Failed to register to ICR core\n");
return PTR_ERR(data->icr_dev);
}
dev_info(dev, "Registered as %s\n", dev_name(data->icr_dev));
return 0;
}
static int aw8637c_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aw8637c_data *data = dev_get_drvdata(dev);
icr_core_unregister(data->icr_dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id of_awinic_aw8637c_match[] = {
{
.compatible = "awinic, aw8637c",
},
{},
};
MODULE_DEVICE_TABLE(of, of_awinic_aw8637c_match);
#endif
static struct platform_driver awinic_aw8637c = {
.probe = aw8637c_probe,
.remove = aw8637c_remove,
.driver = {
.name = "awinic-aw8637c",
.of_match_table = of_match_ptr(of_awinic_aw8637c_match),
},
};
module_platform_driver(awinic_aw8637c);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Awinic AW8637C chip driver");