1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "imx8-gpio.h"
6 
7 #include <stdlib.h>
8 
9 #include <ddk/binding.h>
10 #include <ddk/debug.h>
11 #include <ddk/platform-defs.h>
12 #include <ddk/protocol/platform-device-lib.h>
13 #include <soc/imx8m/imx8m-iomux.h>
14 
15 // Configure a pin for an alternate function specified by fn
imx8m_gpio_set_alt_function(void * ctx,const uint32_t pin,const uint64_t fn)16 static zx_status_t imx8m_gpio_set_alt_function(void* ctx, const uint32_t pin, const uint64_t fn) {
17     imx8_gpio_t* gpio = ctx;
18     iomux_cfg_struct s_cfg = (iomux_cfg_struct)fn;
19 
20     volatile uint8_t* iomux = (volatile uint8_t*)gpio->mmio_iomux.vaddr;
21 
22     zxlogf(SPEW, "0x%lx\n", s_cfg);
23     zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
24            IOMUX_CFG_MUX_MODE_VAL(GET_MUX_MODE_VAL(s_cfg)) |
25                IOMUX_CFG_SION_VAL(GET_SION_VAL(s_cfg)),
26            iomux + GET_MUX_CTL_OFF_VAL(s_cfg));
27     zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
28            IOMUX_CFG_DSE_VAL(GET_DSE_VAL(s_cfg)) |
29                IOMUX_CFG_SRE_VAL(GET_SRE_VAL(s_cfg)) |
30                IOMUX_CFG_ODE_VAL(GET_ODE_VAL(s_cfg)) |
31                IOMUX_CFG_PUE_VAL(GET_PUE_VAL(s_cfg)) |
32                IOMUX_CFG_HYS_VAL(GET_HYS_VAL(s_cfg)) |
33                IOMUX_CFG_LVTTL_VAL(GET_LVTTL_VAL(s_cfg)) |
34                IOMUX_CFG_VSEL_VAL(GET_VSEL_VAL(s_cfg)),
35            iomux + GET_PAD_CTL_OFF_VAL(s_cfg));
36     zxlogf(SPEW, "val = 0x%lx, reg = %p\n",
37            IOMUX_CFG_DAISY_VAL(GET_DAISY_VAL(s_cfg)),
38            iomux + GET_SEL_INP_OFF_VAL(s_cfg));
39 
40     if (GET_MUX_CTL_OFF_VAL(s_cfg)) {
41         writel(
42             IOMUX_CFG_MUX_MODE_VAL(GET_MUX_MODE_VAL(s_cfg)) |
43                 IOMUX_CFG_SION_VAL(GET_SION_VAL(s_cfg)),
44             iomux + GET_MUX_CTL_OFF_VAL(s_cfg));
45     }
46     if (GET_PAD_CTL_OFF_VAL(s_cfg)) {
47         writel(
48             IOMUX_CFG_DSE_VAL(GET_DSE_VAL(s_cfg)) |
49                 IOMUX_CFG_SRE_VAL(GET_SRE_VAL(s_cfg)) |
50                 IOMUX_CFG_ODE_VAL(GET_ODE_VAL(s_cfg)) |
51                 IOMUX_CFG_PUE_VAL(GET_PUE_VAL(s_cfg)) |
52                 IOMUX_CFG_HYS_VAL(GET_HYS_VAL(s_cfg)) |
53                 IOMUX_CFG_LVTTL_VAL(GET_LVTTL_VAL(s_cfg)) |
54                 IOMUX_CFG_VSEL_VAL(GET_VSEL_VAL(s_cfg)),
55             iomux + GET_PAD_CTL_OFF_VAL(s_cfg));
56     }
57     if (GET_SEL_INP_OFF_VAL(s_cfg)) {
58         writel(IOMUX_CFG_DAISY_VAL(GET_DAISY_VAL(s_cfg)),
59                iomux + GET_SEL_INP_OFF_VAL(s_cfg));
60     }
61 
62     return ZX_OK;
63 }
64 
65 static gpio_impl_protocol_ops_t gpio_ops = {
66     .config_in = imx8_gpio_config_in,
67     .config_out = imx8_gpio_config_out,
68     .set_alt_function = imx8m_gpio_set_alt_function,
69     .read = imx8_gpio_read,
70     .write = imx8_gpio_write,
71     .get_interrupt = imx8_gpio_get_interrupt,
72     .release_interrupt = imx8_gpio_release_interrupt,
73     .set_polarity = imx8_gpio_set_polarity,
74 };
75 
imx8m_gpio_release(void * ctx)76 static void imx8m_gpio_release(void* ctx) {
77     unsigned i;
78     imx8_gpio_t* gpio = ctx;
79     mtx_lock(&gpio->gpio_lock);
80     for (i = 0; i < IMX_GPIO_BLOCKS; i++) {
81         mmio_buffer_release(&gpio->mmios[i]);
82     }
83     mmio_buffer_release(&gpio->mmio_iomux);
84 
85     for (int i = 0; i < IMX_GPIO_INTERRUPTS; i++) {
86         zx_interrupt_destroy(gpio->inth[i]);
87         zx_handle_close(gpio->inth[i]);
88     }
89     free(gpio);
90     mtx_unlock(&gpio->gpio_lock);
91 }
92 
93 static zx_protocol_device_t gpio_device_proto = {
94     .version = DEVICE_OPS_VERSION,
95     .release = imx8m_gpio_release,
96 };
97 
imx8m_gpio_bind(void * ctx,zx_device_t * parent)98 static zx_status_t imx8m_gpio_bind(void* ctx, zx_device_t* parent) {
99     zx_status_t status;
100     unsigned i;
101 
102     imx8_gpio_t* gpio = calloc(1, sizeof(imx8_gpio_t));
103     if (!gpio) {
104         return ZX_ERR_NO_MEMORY;
105     }
106 
107     status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &gpio->pdev);
108     if (status != ZX_OK) {
109         zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available %d \n", __FUNCTION__, status);
110         goto fail;
111     }
112 
113     status = device_get_protocol(parent, ZX_PROTOCOL_PBUS, &gpio->pbus);
114     if (status != ZX_OK) {
115         zxlogf(ERROR, "%s: ZX_PROTOCOL_PBUS not available %d\n", __FUNCTION__, status);
116         goto fail;
117     }
118 
119     for (i = 0; i < IMX_GPIO_BLOCKS; i++) {
120         status = pdev_map_mmio_buffer2(&gpio->pdev, i, ZX_CACHE_POLICY_UNCACHED_DEVICE,
121                                        &gpio->mmios[i]);
122         if (status != ZX_OK) {
123             zxlogf(ERROR, "%s: pdev_map_mmio_buffer gpio failed %d\n", __FUNCTION__, status);
124             goto fail;
125         }
126 
127         mtx_init(&gpio->lock[i], mtx_plain);
128     }
129 
130     status = pdev_map_mmio_buffer2(&gpio->pdev, IMX_GPIO_BLOCKS, ZX_CACHE_POLICY_UNCACHED_DEVICE,
131                                    &gpio->mmio_iomux);
132     if (status != ZX_OK) {
133         zxlogf(ERROR, "%s: pdev_map_mmio_buffer iomux failed %d\n", __FUNCTION__, status);
134         goto fail;
135     }
136 
137     pdev_device_info_t info;
138     status = pdev_get_device_info(&gpio->pdev, &info);
139     if (status != ZX_OK) {
140         zxlogf(ERROR, "%s: pdev_get_device_info failed %d\n", __FUNCTION__, status);
141         goto fail;
142     }
143 
144     status = zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &gpio->porth);
145     if (status != ZX_OK) {
146         zxlogf(ERROR, "%s: zx_port_create failed %d\n", __FUNCTION__, status);
147         goto fail;
148     }
149 
150     for (i = 0; i < info.irq_count; i++) {
151         // Create Interrupt Object
152         status = pdev_map_interrupt(&gpio->pdev, i,
153                                     &gpio->inth[i]);
154         if (status != ZX_OK) {
155             zxlogf(ERROR, "%s: pdev_map_interrupt failed %d\n", __FUNCTION__, status);
156             goto fail;
157         }
158         // The KEY is the Interrupt Number for our usecase
159         status = zx_interrupt_bind(gpio->inth[i], gpio->porth, i, 0 /*optons*/);
160         if (status != ZX_OK) {
161             zxlogf(ERROR, "%s: zx_interrupt_bind failed %d\n", __FUNCTION__, status);
162             goto fail;
163         }
164     }
165 
166     thrd_create_with_name(&gpio->irq_handler, imx8_gpio_irq_handler, gpio, "imx8_gpio_irq_handler");
167 
168     device_add_args_t args = {
169         .version = DEVICE_ADD_ARGS_VERSION,
170         .name = "imx8-gpio",
171         .ctx = gpio,
172         .ops = &gpio_device_proto,
173         .flags = DEVICE_ADD_NON_BINDABLE,
174     };
175 
176     status = device_add(parent, &args, &gpio->zxdev);
177     if (status != ZX_OK) {
178         zxlogf(ERROR, "%s: device_add failed! %d\n", __FUNCTION__, status);
179         goto fail;
180     }
181 
182     gpio->gpio.ops = &gpio_ops;
183     gpio->gpio.ctx = gpio;
184     const platform_proxy_cb_t kCallback = {NULL, NULL};
185     pbus_register_protocol(&gpio->pbus, ZX_PROTOCOL_GPIO_IMPL, &gpio->gpio, sizeof(gpio->gpio),
186                            &kCallback);
187 
188     return ZX_OK;
189 
190 fail:
191     imx8m_gpio_release(gpio);
192     return status;
193 }
194 
195 static zx_driver_ops_t imx8m_gpio_driver_ops = {
196     .version = DRIVER_OPS_VERSION,
197     .bind = imx8m_gpio_bind,
198 };
199 
200 ZIRCON_DRIVER_BEGIN(imx8m_gpio, imx8m_gpio_driver_ops, "zircon", "0.1", 4)
201     BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
202     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_NXP),
203     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_IMX_GPIO),
204     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_IMX8MEVK),
205 ZIRCON_DRIVER_END(imx8m_gpio)
206