blob: 0d686588ed6d01b7170bf6e5b37edd0baf608051 [file] [log] [blame]
/*
* Google LWIS GPIO Interface
*
* Copyright (c) 2018 Google, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME "-gpio: " fmt
#include <linux/gpio.h>
#include <linux/kernel.h>
#include "lwis_gpio.h"
#include "lwis_interrupt.h"
/* debug function */
void lwis_gpio_list_print(char *name, struct gpio_descs *gpios)
{
int i;
if (IS_ERR_OR_NULL(gpios)) {
pr_info("name: %s error: %ld\n", name, PTR_ERR(gpios));
} else {
pr_info("name: %s, count: %d\n", name, gpios->ndescs);
for (i = 0; i < gpios->ndescs; i++) {
pr_info("gpio number: %d\n", desc_to_gpio(gpios->desc[i]));
}
}
}
struct gpio_descs *lwis_gpio_list_get(struct device *dev, const char *name)
{
/* By default, the GPIO pins are acquired but uninitialized */
return devm_gpiod_get_array(dev, name, GPIOD_ASIS);
}
void lwis_gpio_list_put(struct gpio_descs *gpios, struct device *dev)
{
devm_gpiod_put_array(dev, gpios);
}
int lwis_gpio_list_set_output_value(struct gpio_descs *gpios, int value)
{
int i;
int ret;
if (!gpios) {
return -EINVAL;
}
for (i = 0; i < gpios->ndescs; ++i) {
ret = gpiod_direction_output(gpios->desc[i], value);
if (ret) {
pr_err("Failed to set value for GPIO %d\n", i);
return ret;
}
}
return 0;
}
int lwis_gpio_list_set_output_value_raw(struct gpio_descs *gpios, int value)
{
int i;
int ret;
if (!gpios) {
return -EINVAL;
}
for (i = 0; i < gpios->ndescs; ++i) {
ret = gpiod_direction_output_raw(gpios->desc[i], value);
if (ret) {
pr_err("Failed to set value for GPIO %d\n", i);
return ret;
}
}
return 0;
}
int lwis_gpio_list_set_input(struct gpio_descs *gpios)
{
int i;
int ret;
if (!gpios) {
return -EINVAL;
}
for (i = 0; i < gpios->ndescs; ++i) {
ret = gpiod_direction_input(gpios->desc[i]);
if (ret) {
pr_err("Failed to set GPIO %d to input\n", i);
return ret;
}
}
return 0;
}
int lwis_gpio_list_to_irqs(struct lwis_device *lwis_dev, struct lwis_gpios_info *gpios_info,
char *irq_gpios_names)
{
struct gpio_descs *gpios;
struct lwis_interrupt_list *irq_list;
int i;
int irq;
if (!lwis_dev || !gpios_info) {
return -EINVAL;
}
gpios = gpios_info->gpios;
if (!gpios) {
return 0;
}
irq_list = lwis_interrupt_list_alloc(lwis_dev, gpios->ndescs);
if (IS_ERR(irq_list)) {
pr_err("Failed to allocate irq list\n");
return PTR_ERR(irq_list);
}
for (i = 0; i < gpios->ndescs; ++i) {
char *name;
irq = gpiod_to_irq(gpios->desc[i]);
if (irq < 0) {
pr_err("gpio to irq failed (%d)\n", irq);
lwis_interrupt_list_free(irq_list);
return irq;
}
name = irq_gpios_names + i * LWIS_MAX_NAME_STRING_LEN;
lwis_interrupt_get_gpio_irq(irq_list, i, name, irq);
}
gpios_info->irq_list = irq_list;
return 0;
}
struct lwis_gpios_list *lwis_gpios_list_alloc(int count)
{
struct lwis_gpios_list *list;
/* No need to allocate if count is invalid */
if (count <= 0) {
return ERR_PTR(-EINVAL);
}
list = kmalloc(sizeof(struct lwis_gpios_list), GFP_KERNEL);
if (!list) {
pr_err("Failed to allocate gpios list\n");
return ERR_PTR(-ENOMEM);
}
list->gpios_info = kmalloc(count * sizeof(struct lwis_gpios_info), GFP_KERNEL);
if (!list->gpios_info) {
pr_err("Failed to allocate lwis_gpios_info instances\n");
kfree(list);
return ERR_PTR(-ENOMEM);
}
list->count = count;
return list;
}
void lwis_gpios_list_free(struct lwis_gpios_list *list)
{
if (!list) {
return;
}
if (list->gpios_info->irq_list) {
lwis_interrupt_list_free(list->gpios_info->irq_list);
}
if (list->gpios_info) {
kfree(list->gpios_info);
}
kfree(list);
}
struct lwis_gpios_info *lwis_gpios_get_info_by_name(struct lwis_gpios_list *list, char *name)
{
int i;
if (!list || !name) {
return ERR_PTR(-EINVAL);
}
for (i = 0; i < list->count; ++i) {
if (!strcmp(list->gpios_info[i].name, name)) {
return &list->gpios_info[i];
}
}
return ERR_PTR(-EINVAL);
}