| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /*
- * 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 <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #include "dev_pin_dm.h"
- #define PL061_DIR 0x400
- #define PL061_IS 0x404
- #define PL061_IBE 0x408
- #define PL061_IEV 0x40c
- #define PL061_IE 0x410
- #define PL061_RIS 0x414
- #define PL061_MIS 0x418
- #define PL061_IC 0x41c
- #define PL061_GPIO_NR 8
- struct pl061
- {
- struct rt_device_pin parent;
- int irq;
- void *base;
- struct rt_clk *pclk;
- struct rt_spinlock spinlock;
- };
- #define raw_to_pl061(raw) rt_container_of(raw, struct pl061, parent)
- rt_inline rt_uint8_t pl061_read(struct pl061 *pl061, int offset)
- {
- return HWREG8(pl061->base + offset);
- }
- rt_inline void pl061_write(struct pl061 *pl061, int offset, rt_uint8_t value)
- {
- HWREG8(pl061->base + offset) = value;
- }
- static void pl061_isr(int irqno, void *param)
- {
- rt_uint8_t mask = 0;
- rt_ubase_t pending, level;
- struct pl061 *pl061 = (struct pl061 *)param;
- level = rt_spin_lock_irqsave(&pl061->spinlock);
- pending = pl061_read(pl061, PL061_MIS);
- rt_spin_unlock_irqrestore(&pl061->spinlock, level);
- if (pending)
- {
- for (int pin = 0; pin < PL061_GPIO_NR; ++pin)
- {
- if (pending & RT_BIT(pin))
- {
- mask |= RT_BIT(pin);
- pin_pic_handle_isr(&pl061->parent, pin);
- }
- }
- level = rt_spin_lock_irqsave(&pl061->spinlock);
- pl061_write(pl061, PL061_IC, mask);
- rt_spin_unlock_irqrestore(&pl061->spinlock, level);
- }
- }
- static void pl061_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode)
- {
- struct pl061 *pl061 = raw_to_pl061(device);
- if (pin >= 0 && pin < PL061_GPIO_NR)
- {
- rt_base_t level = rt_spin_lock_irqsave(&pl061->spinlock);
- switch (mode)
- {
- case PIN_MODE_OUTPUT:
- pl061_write(pl061, RT_BIT(pin + 2), 1 << pin);
- pl061_write(pl061, PL061_DIR, pl061_read(pl061, PL061_DIR) | RT_BIT(pin));
- /*
- * gpio value is set again, because pl061 doesn't allow to set value
- * of a gpio pin before configuring it in OUT mode.
- */
- pl061_write(pl061, RT_BIT(pin + 2), 1 << pin);
- break;
- case PIN_MODE_INPUT:
- pl061_write(pl061, PL061_DIR, pl061_read(pl061, PL061_DIR) & ~RT_BIT(pin));
- break;
- default:
- break;
- }
- rt_spin_unlock_irqrestore(&pl061->spinlock, level);
- }
- }
- static void pl061_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value)
- {
- struct pl061 *pl061 = raw_to_pl061(device);
- if (pin >= 0 && pin < PL061_GPIO_NR)
- {
- pl061_write(pl061, RT_BIT(pin + 2), !!value << pin);
- }
- }
- static rt_ssize_t pl061_pin_read(struct rt_device *device, rt_base_t pin)
- {
- rt_int8_t value = -RT_EINVAL;
- struct pl061 *pl061 = raw_to_pl061(device);
- if (pin >= 0 && pin < PL061_GPIO_NR)
- {
- value = !!pl061_read(pl061, RT_BIT(pin + 2));
- }
- return value;
- }
- static rt_err_t pl061_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
- {
- rt_err_t err = RT_EOK;
- struct pl061 *pl061 = raw_to_pl061(device);
- if (pin >= 0 && pin < PL061_GPIO_NR)
- {
- rt_uint8_t gpioie, mask = RT_BIT(pin);
- rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock);
- if (enabled)
- {
- gpioie = pl061_read(pl061, PL061_IE) | mask;
- }
- else
- {
- gpioie = pl061_read(pl061, PL061_IE) & ~mask;
- }
- pl061_write(pl061, PL061_IE, gpioie);
- rt_spin_unlock_irqrestore(&pl061->spinlock, level);
- }
- else
- {
- err = -RT_EINVAL;
- }
- return err;
- }
- static rt_err_t pl061_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode)
- {
- rt_err_t err = RT_EOK;
- struct pl061 *pl061 = raw_to_pl061(device);
- if (pin >= 0 && pin < PL061_GPIO_NR)
- {
- rt_uint8_t gpiois, gpioibe, gpioiev, bit = RT_BIT(pin);
- rt_ubase_t level = rt_spin_lock_irqsave(&pl061->spinlock);
- gpioiev = pl061_read(pl061, PL061_IEV);
- gpiois = pl061_read(pl061, PL061_IS);
- gpioibe = pl061_read(pl061, PL061_IBE);
- if (mode == PIN_IRQ_MODE_HIGH_LEVEL || mode == PIN_IRQ_MODE_LOW_LEVEL)
- {
- rt_bool_t polarity = (mode == PIN_IRQ_MODE_HIGH_LEVEL);
- /* Disable edge detection */
- gpioibe &= ~bit;
- /* Enable level detection */
- gpiois |= bit;
- /* Select polarity */
- if (polarity)
- {
- gpioiev |= bit;
- }
- else
- {
- gpioiev &= ~bit;
- }
- }
- else if (mode == PIN_IRQ_MODE_RISING_FALLING)
- {
- /* Disable level detection */
- gpiois &= ~bit;
- /* Select both edges, setting this makes PL061_EV be ignored */
- gpioibe |= bit;
- }
- else if (mode == PIN_IRQ_MODE_RISING || mode == PIN_IRQ_MODE_FALLING)
- {
- rt_bool_t rising = (mode == PIN_IRQ_MODE_RISING);
- /* Disable level detection */
- gpiois &= ~bit;
- /* Clear detection on both edges */
- gpioibe &= ~bit;
- /* Select edge */
- if (rising)
- {
- gpioiev |= bit;
- }
- else
- {
- gpioiev &= ~bit;
- }
- }
- else
- {
- /* No trigger: disable everything */
- gpiois &= ~bit;
- gpioibe &= ~bit;
- gpioiev &= ~bit;
- }
- pl061_write(pl061, PL061_IS, gpiois);
- pl061_write(pl061, PL061_IBE, gpioibe);
- pl061_write(pl061, PL061_IEV, gpioiev);
- rt_spin_unlock_irqrestore(&pl061->spinlock, level);
- }
- else
- {
- err = -RT_EINVAL;
- }
- return err;
- }
- static const struct rt_pin_ops pl061_pin_ops =
- {
- .pin_mode = pl061_pin_mode,
- .pin_write = pl061_pin_write,
- .pin_read = pl061_pin_read,
- .pin_irq_enable = pl061_pin_irq_enable,
- .pin_irq_mode = pl061_pin_irq_mode,
- };
- static rt_err_t pl061_probe(struct rt_platform_device *pdev)
- {
- rt_err_t err;
- struct rt_device *dev = &pdev->parent;
- struct pl061 *pl061 = rt_calloc(1, sizeof(*pl061));
- if (!pl061)
- {
- return -RT_ENOMEM;
- }
- pl061->base = rt_dm_dev_iomap(dev, 0);
- if (!pl061->base)
- {
- err = -RT_EIO;
- goto _fail;
- }
- pl061->irq = rt_dm_dev_get_irq(dev, 0);
- if (pl061->irq < 0)
- {
- err = pl061->irq;
- goto _fail;
- }
- pl061->pclk = rt_clk_get_by_name(dev, "apb_pclk");
- if (rt_is_err(pl061->pclk))
- {
- err = rt_ptr_err(pl061->pclk);
- goto _fail;
- }
- if ((err = rt_clk_prepare_enable(pl061->pclk)))
- {
- goto _fail;
- }
- rt_dm_dev_bind_fwdata(dev, RT_NULL, &pl061->parent);
- rt_spin_lock_init(&pl061->spinlock);
- pl061->parent.ops = &pl061_pin_ops;
- pin_api_init(&pl061->parent, PL061_GPIO_NR);
- pin_pic_init(&pl061->parent, pl061->irq);
- rt_hw_interrupt_install(pl061->irq, pl061_isr, pl061, "gpio-pl061");
- rt_hw_interrupt_umask(pl061->irq);
- return RT_EOK;
- _fail:
- if (pl061->base)
- {
- rt_iounmap(pl061->base);
- }
- if (!rt_is_err_or_null(pl061->pclk))
- {
- rt_clk_disable_unprepare(pl061->pclk);
- rt_clk_put(pl061->pclk);
- }
- rt_free(pl061);
- return err;
- }
- static const struct rt_ofw_node_id pl061_ofw_ids[] =
- {
- { .compatible = "arm,pl061" },
- { /* sentinel */ }
- };
- static struct rt_platform_driver pl061_driver =
- {
- .name = "pin-pl061",
- .ids = pl061_ofw_ids,
- .probe = pl061_probe,
- };
- static int pl061_drv_register(void)
- {
- rt_platform_driver_register(&pl061_driver);
- return 0;
- }
- INIT_SUBSYS_EXPORT(pl061_drv_register);
|