GuEe-GUI e3340eca34 [DM/FEATURE] Support virtual pin
1. There is only one GPIO device in System.
2. For Pin API input is pin number
3. Add sets pin debounce time API.

So we need a virtual pin for multi gpio chip.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
2024-12-02 21:15:17 +08:00

459 lines
10 KiB
C

/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-26 GuEe-GUI first version
*/
#include "dev_pin_dm.h"
static rt_size_t pin_total_nr = 0;
static struct rt_spinlock pin_lock = {};
static rt_list_t pin_nodes = RT_LIST_OBJECT_INIT(pin_nodes);
static struct rt_device_pin *pin_device_find(rt_ubase_t pin)
{
struct rt_device_pin *gpio = RT_NULL, *gpio_tmp;
rt_spin_lock(&pin_lock);
rt_list_for_each_entry(gpio_tmp, &pin_nodes, list)
{
if (pin >= gpio_tmp->pin_start &&
pin - gpio_tmp->pin_start < gpio_tmp->pin_nr)
{
gpio = gpio_tmp;
break;
}
}
rt_spin_unlock(&pin_lock);
return gpio;
}
static void pin_api_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_mode)
{
gpio->ops->pin_mode(&gpio->parent, pin - gpio->pin_start, mode);
}
}
static void pin_api_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_write)
{
gpio->ops->pin_write(&gpio->parent, pin - gpio->pin_start, value);
}
}
static rt_ssize_t pin_api_read(struct rt_device *device, rt_base_t pin)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_read)
{
return gpio->ops->pin_read(&gpio->parent, pin - gpio->pin_start);
}
return -RT_EINVAL;
}
static rt_err_t pin_api_attach_irq(struct rt_device *device, rt_base_t pin,
rt_uint8_t mode, void (*hdr)(void *args), void *args)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio)
{
rt_base_t pin_index = pin - gpio->pin_start;
if (!gpio->ops->pin_attach_irq)
{
rt_err_t err;
struct rt_pin_irq_hdr *legacy_isr;
if ((err = gpio->ops->pin_irq_mode(&gpio->parent, pin_index, mode)))
{
return err;
}
legacy_isr = &gpio->legacy_isr[pin_index];
legacy_isr->pin = pin_index;
legacy_isr->mode = mode;
legacy_isr->hdr = hdr;
legacy_isr->args = args;
return RT_EOK;
}
else
{
return gpio->ops->pin_attach_irq(&gpio->parent, pin_index, mode, hdr, args);
}
}
return -RT_EINVAL;
}
static rt_err_t pin_api_detach_irq(struct rt_device *device, rt_base_t pin)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio)
{
rt_base_t pin_index = pin - gpio->pin_start;
if (!gpio->ops->pin_detach_irq)
{
struct rt_pin_irq_hdr *legacy_isr;
legacy_isr = &gpio->legacy_isr[pin_index];
rt_memset(legacy_isr, 0, sizeof(*legacy_isr));
return RT_EOK;
}
else
{
return gpio->ops->pin_detach_irq(&gpio->parent, pin);
}
}
return -RT_EINVAL;
}
static rt_err_t pin_api_irq_enable(struct rt_device *device, rt_base_t pin,
rt_uint8_t enabled)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_irq_enable)
{
return gpio->ops->pin_irq_enable(&gpio->parent, pin - gpio->pin_start, enabled);
}
return -RT_EINVAL;
}
static rt_base_t pin_api_get(const char *name)
{
rt_base_t res = -RT_EINVAL;
struct rt_device_pin *gpio;
rt_spin_lock(&pin_lock);
rt_list_for_each_entry(gpio, &pin_nodes, list)
{
if (gpio->ops->pin_get && !(res = gpio->ops->pin_get(name)))
{
break;
}
}
rt_spin_unlock(&pin_lock);
return res;
}
static rt_err_t pin_api_debounce(struct rt_device *device, rt_base_t pin,
rt_uint32_t debounce)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_debounce)
{
return gpio->ops->pin_debounce(&gpio->parent, pin - gpio->pin_start, debounce);
}
return -RT_EINVAL;
}
static rt_err_t pin_api_irq_mode(struct rt_device *device, rt_base_t pin,
rt_uint8_t mode)
{
struct rt_device_pin *gpio = pin_device_find(pin);
if (gpio && gpio->ops->pin_irq_mode)
{
return gpio->ops->pin_irq_mode(&gpio->parent, pin - gpio->pin_start, mode);
}
return -RT_EINVAL;
}
static const struct rt_pin_ops pin_api_dm_ops =
{
.pin_mode = pin_api_mode,
.pin_write = pin_api_write,
.pin_read = pin_api_read,
.pin_attach_irq = pin_api_attach_irq,
.pin_detach_irq = pin_api_detach_irq,
.pin_irq_enable = pin_api_irq_enable,
.pin_get = pin_api_get,
.pin_debounce = pin_api_debounce,
.pin_irq_mode = pin_api_irq_mode,
};
rt_err_t pin_api_init(struct rt_device_pin *gpio, rt_size_t pin_nr)
{
rt_err_t err = RT_EOK;
if (!gpio || !gpio->ops)
{
return -RT_EINVAL;
}
rt_spin_lock(&pin_lock);
if (rt_list_isempty(&pin_nodes))
{
rt_spin_unlock(&pin_lock);
rt_device_pin_register("gpio", &pin_api_dm_ops, RT_NULL);
rt_spin_lock(&pin_lock);
}
gpio->pin_start = pin_total_nr;
gpio->pin_nr = pin_nr;
pin_total_nr += pin_nr;
rt_list_init(&gpio->list);
rt_list_insert_before(&pin_nodes, &gpio->list);
rt_spin_unlock(&pin_lock);
return err;
}
static void pin_dm_irq_mask(struct rt_pic_irq *pirq)
{
struct rt_device_pin *gpio = pirq->pic->priv_data;
gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 0);
}
static void pin_dm_irq_unmask(struct rt_pic_irq *pirq)
{
struct rt_device_pin *gpio = pirq->pic->priv_data;
gpio->ops->pin_irq_enable(&gpio->parent, pirq->hwirq, 1);
}
static rt_err_t pin_dm_irq_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode)
{
rt_uint8_t pin_mode;
struct rt_device_pin *gpio = pirq->pic->priv_data;
switch (mode)
{
case RT_IRQ_MODE_EDGE_RISING:
pin_mode = PIN_IRQ_MODE_RISING;
break;
case RT_IRQ_MODE_EDGE_FALLING:
pin_mode = PIN_IRQ_MODE_FALLING;
break;
case RT_IRQ_MODE_EDGE_BOTH:
pin_mode = PIN_IRQ_MODE_RISING_FALLING;
break;
case RT_IRQ_MODE_LEVEL_HIGH:
pin_mode = PIN_IRQ_MODE_HIGH_LEVEL;
break;
case RT_IRQ_MODE_LEVEL_LOW:
pin_mode = PIN_IRQ_MODE_LOW_LEVEL;
break;
default:
return -RT_ENOSYS;
}
return gpio->ops->pin_irq_mode(&gpio->parent, pirq->hwirq, pin_mode);
}
static int pin_dm_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode)
{
int irq = -1;
struct rt_device_pin *gpio = pic->priv_data;
struct rt_pic_irq *pirq = rt_pic_find_irq(pic, hwirq);
if (pirq)
{
irq = rt_pic_config_irq(pic, hwirq, hwirq);
if (irq >= 0)
{
rt_pic_cascade(pirq, gpio->irqchip.irq);
rt_pic_irq_set_triger_mode(irq, mode);
}
}
return irq;
}
static rt_err_t pin_dm_irq_parse(struct rt_pic *pic,
struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq)
{
rt_err_t err = RT_EOK;
if (args->args_count == 2)
{
out_pirq->hwirq = args->args[0];
out_pirq->mode = args->args[1] & RT_IRQ_MODE_MASK;
}
else
{
err = -RT_EINVAL;
}
return err;
}
const static struct rt_pic_ops pin_dm_ops =
{
.name = "GPIO",
.irq_enable = pin_dm_irq_mask,
.irq_disable = pin_dm_irq_unmask,
.irq_mask = pin_dm_irq_mask,
.irq_unmask = pin_dm_irq_unmask,
.irq_set_triger_mode = pin_dm_irq_set_triger_mode,
.irq_map = pin_dm_irq_map,
.irq_parse = pin_dm_irq_parse,
};
rt_err_t pin_pic_handle_isr(struct rt_device_pin *gpio, rt_base_t pin)
{
rt_err_t err;
if (gpio)
{
rt_ubase_t pin_index = pin;
struct rt_pin_irqchip *irqchip = &gpio->irqchip;
if (pin_index < gpio->pin_nr)
{
struct rt_pic_irq *pirq;
struct rt_pin_irq_hdr *legacy_isr;
pirq = rt_pic_find_irq(&irqchip->parent, pin_index);
if (pirq->irq >= 0)
{
err = rt_pic_handle_isr(pirq);
}
else
{
err = -RT_EINVAL;
}
legacy_isr = &gpio->legacy_isr[pin_index];
if (legacy_isr->hdr)
{
legacy_isr->hdr(legacy_isr->args);
}
}
else
{
err = -RT_EINVAL;
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
rt_err_t pin_pic_init(struct rt_device_pin *gpio, int pin_irq)
{
rt_err_t err;
if (gpio)
{
struct rt_pin_irqchip *irqchip = &gpio->irqchip;
struct rt_pic *pic = &irqchip->parent;
irqchip->irq = pin_irq;
if (!gpio->pin_nr)
{
return -RT_EINVAL;
}
gpio->legacy_isr = rt_calloc(gpio->pin_nr, sizeof(*gpio->legacy_isr));
if (!gpio->legacy_isr)
{
return -RT_ENOMEM;
}
pic->priv_data = gpio;
pic->ops = &pin_dm_ops;
/* Make sure the type of gpio for pic */
gpio->parent.parent.type = RT_Object_Class_Device;
rt_pic_default_name(&irqchip->parent);
err = rt_pic_linear_irq(pic, gpio->pin_nr);
rt_pic_user_extends(pic);
err = RT_EOK;
}
else
{
err = -RT_EINVAL;
}
return err;
}
rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, const char *propname, int index,
rt_uint8_t *out_mode, rt_uint8_t *out_value)
{
rt_ssize_t res = -RT_ENOSYS;
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
res = rt_ofw_get_named_pin(dev->ofw_node, propname, index, out_mode, out_value);
}
else
{
res = -RT_EINVAL;
}
#endif /* RT_USING_OFW */
return res;
}
rt_ssize_t rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname)
{
rt_ssize_t count = -RT_ENOSYS;
RT_ASSERT(dev != RT_NULL);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
count = rt_ofw_get_named_pin_count(dev->ofw_node, propname);
}
else
{
count = -RT_EINVAL;
}
#endif /* RT_USING_OFW */
return count;
}