Skip to content

pps-gpio: switch from device tree to module parameters #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 20, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 79 additions & 93 deletions drivers/pps/clients/pps-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,46 +41,52 @@
#define AMLGPIO_IRQ_BASE 96

/* Info for each registered platform device */
struct pps_gpio_device_data {
static struct pps_gpio_device_data {
int irq; /* IRQ used as PPS source */
struct pps_device *pps; /* PPS source device */
struct pps_source_info info; /* PPS source information */
bool assert_falling_edge;
bool capture_clear;
unsigned int gpio_pin;
};
} pps_data;

// Default GPIO. Use parameters during module load to override.
// note: this only handles one instance of pps-gpio. If you want more, use the devicetree version
static int gpio_pin = 238; // GPIOX_10 / pin 12
module_param(gpio_pin, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(gpio_pin, "GPIO pin number (\"Export GPIO\" type), default=238 (GPIOX_10 / pin 12)");

static int assert_falling_edge = 0;
module_param(assert_falling_edge, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(assert_falling_edge, "use the falling edge instead of the rising edge, default=0 (rising)");

/*
* Report the PPS event
*/

static irqreturn_t pps_gpio_irq_handler(int irq, void *data)
{
const struct pps_gpio_device_data *info;
struct pps_event_time ts;
int rising_edge;

/* Get the time stamp first */
pps_get_ts(&ts);

info = data;

rising_edge = gpio_get_value(info->gpio_pin);
if ((rising_edge && !info->assert_falling_edge) ||
(!rising_edge && info->assert_falling_edge))
pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL);
else if (info->capture_clear &&
((rising_edge && info->assert_falling_edge) ||
(!rising_edge && !info->assert_falling_edge)))
pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL);
rising_edge = gpio_get_value(pps_data.gpio_pin);
if ((rising_edge && !assert_falling_edge) ||
(!rising_edge && assert_falling_edge))
pps_event(pps_data.pps, &ts, PPS_CAPTUREASSERT, NULL);
else if (pps_data.capture_clear &&
((rising_edge && assert_falling_edge) ||
(!rising_edge && !assert_falling_edge)))
pps_event(pps_data.pps, &ts, PPS_CAPTURECLEAR, NULL);

return IRQ_HANDLED;
}

static unsigned long
get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
{
unsigned long flags = data->assert_falling_edge ?
unsigned long flags = assert_falling_edge ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;

if (data->capture_clear) {
Expand All @@ -91,7 +97,7 @@ get_irqf_trigger_flags(const struct pps_gpio_device_data *data)
return flags;
}

static int pps_gpio_irq_setup(unsigned int gpio_pin, bool assert_falling_edge)
static int pps_gpio_irq_setup(unsigned int gpio_pin)
{
struct gpio_chip *chip;
int ret;
Expand All @@ -118,131 +124,111 @@ static int pps_gpio_irq_setup(unsigned int gpio_pin, bool assert_falling_edge)
return -EINVAL;
}

static int pps_gpio_probe(struct platform_device *pdev)
static void pps_gpio_release(struct device *dev) {
// does nothing, everything is allocated as part of the module
}

static struct device pps_gpio_dev = {
.id = 0,
.parent = &platform_bus,
.release = &pps_gpio_release
};

static int __init pps_gpio_init(void)
{
struct pps_gpio_device_data *data;
const char *gpio_label;
int ret;
int pps_default_params;
const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data;
struct device_node *np = pdev->dev.of_node;

/* allocate space for device info */
data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;

if (pdata) {
data->gpio_pin = pdata->gpio_pin;
gpio_label = pdata->gpio_label;

data->assert_falling_edge = pdata->assert_falling_edge;
data->capture_clear = pdata->capture_clear;
} else {
ret = of_get_gpio(np, 0);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get GPIO from device tree\n");
return ret;
}
data->gpio_pin = ret;
gpio_label = PPS_GPIO_NAME;

if (of_get_property(np, "assert-falling-edge", NULL))
data->assert_falling_edge = true;

/* register the device */
ret = dev_set_name(&pps_gpio_dev, "%s%d", PPS_GPIO_NAME, pps_gpio_dev.id);
if(ret < 0) {
pr_err("dev_set_name failed with %d\n", ret);
return ret;
}
ret = device_register(&pps_gpio_dev);
if(ret < 0) {
pr_err("device_register failed with %d\n", ret);
return ret;
}

pps_data.gpio_pin = gpio_pin;

/* GPIO setup */
ret = devm_gpio_request(&pdev->dev, data->gpio_pin, gpio_label);
ret = devm_gpio_request(&pps_gpio_dev, pps_data.gpio_pin, "PPS-GPIO");
if (ret) {
dev_err(&pdev->dev, "failed to request GPIO %u\n",
data->gpio_pin);
dev_err(&pps_gpio_dev, "failed to request GPIO %u\n",
pps_data.gpio_pin);
return ret;
}

ret = gpio_direction_input(data->gpio_pin);
ret = gpio_direction_input(pps_data.gpio_pin);
if (ret) {
dev_err(&pdev->dev, "failed to set pin direction\n");
dev_err(&pps_gpio_dev, "failed to set pin direction\n");
return -EINVAL;
}

/* IRQ setup */
ret = pps_gpio_irq_setup(data->gpio_pin, data->assert_falling_edge);
ret = pps_gpio_irq_setup(pps_data.gpio_pin);
if (ret < 0) {
dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret);
dev_err(&pps_gpio_dev, "failed to map GPIO to IRQ: %d\n", ret);
return -EINVAL;
}
data->irq = ret;
pps_data.irq = ret;

/* initialize PPS specific parts of the bookkeeping data structure. */
data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
pps_data.info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT |
PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC;
if (data->capture_clear)
data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
if (pps_data.capture_clear)
pps_data.info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR |
PPS_ECHOCLEAR;
data->info.owner = THIS_MODULE;
snprintf(data->info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
pdev->name, pdev->id);
pps_data.info.owner = THIS_MODULE;
snprintf(pps_data.info.name, PPS_MAX_NAME_LEN - 1, "%s.%d",
PPS_GPIO_NAME, pps_gpio_dev.id);

/* register PPS source */
pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
if (data->capture_clear)
if (pps_data.capture_clear)
pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
data->pps = pps_register_source(&data->info, pps_default_params);
if (data->pps == NULL) {
dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n",
data->irq);
pps_data.pps = pps_register_source(&pps_data.info, pps_default_params);
if (pps_data.pps == NULL) {
dev_err(&pps_gpio_dev, "failed to register IRQ %d as PPS source\n",
pps_data.irq);
return -EINVAL;
}

/* register IRQ interrupt handler */
ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler,
get_irqf_trigger_flags(data), data->info.name, data);
ret = devm_request_irq(&pps_gpio_dev, pps_data.irq, pps_gpio_irq_handler,
get_irqf_trigger_flags(&pps_data), pps_data.info.name, NULL);
if (ret) {
pps_unregister_source(data->pps);
dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq);
pps_unregister_source(pps_data.pps);
dev_err(&pps_gpio_dev, "failed to acquire IRQ %d\n", pps_data.irq);
return -EINVAL;
}

platform_set_drvdata(pdev, data);
dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n",
data->irq);
dev_info(pps_data.pps->dev, "Registered IRQ %d as PPS source\n",
pps_data.irq);

return 0;
}

static int pps_gpio_remove(struct platform_device *pdev)
static void __exit pps_gpio_exit(void)
{
unsigned int gpio_pin;
int irq_banks[2] = {0, 0};
struct pps_gpio_device_data *data = platform_get_drvdata(pdev);

// gpio_to_irq translates from global GPIO # to chip offset, which meson_free_irq wants
gpio_pin = gpio_to_irq(data->gpio_pin);
gpio_pin = gpio_to_irq(pps_data.gpio_pin);
meson_free_irq(gpio_pin, irq_banks);

pps_unregister_source(data->pps);
dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq);
return 0;
pps_unregister_source(pps_data.pps);
dev_info(&pps_gpio_dev, "removed IRQ %d as PPS source\n", pps_data.irq);

device_unregister(&pps_gpio_dev);
}

static const struct of_device_id pps_gpio_dt_ids[] = {
{ .compatible = "pps-gpio", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids);

static struct platform_driver pps_gpio_driver = {
.probe = pps_gpio_probe,
.remove = pps_gpio_remove,
.driver = {
.name = PPS_GPIO_NAME,
.owner = THIS_MODULE,
.of_match_table = pps_gpio_dt_ids,
},
};
module_init(pps_gpio_init);
module_exit(pps_gpio_exit);

module_platform_driver(pps_gpio_driver);
MODULE_AUTHOR("Ricardo Martins <[email protected]>");
MODULE_AUTHOR("James Nuss <[email protected]>");
MODULE_DESCRIPTION("Use GPIO pin as PPS source");
Expand Down