1 // Copyright 2017 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 <hw/reg.h>
6 #include <limits.h>
7 #include <stdio.h>
8 
9 #include <gpio/pl061/pl061.h>
10 
11 // GPIO register offsets
12 #define GPIODATA(mask)  ((mask) << 2)   // Data registers, mask provided as index
13 #define GPIODIR         0x400           // Data direction register (0 = IN, 1 = OUT)
14 #define GPIOIS          0x404           // Interrupt sense register (0 = edge, 1 = level)
15 #define GPIOIBE         0x408           // Interrupt both edges register (1 = both)
16 #define GPIOIEV         0x40C           // Interrupt event register (0 = falling, 1 = rising)
17 #define GPIOIE          0x410           // Interrupt mask register (1 = interrupt masked)
18 #define GPIORIS         0x414           // Raw interrupt status register
19 #define GPIOMIS         0x418           // Masked interrupt status register
20 #define GPIOIC          0x41C           // Interrupt clear register
21 #define GPIOAFSEL       0x420           // Mode control select register
22 
23 #define GPIOS_PER_PAGE  8
24 
pl061_gpio_config_in(void * ctx,uint32_t index,uint32_t flags)25 static zx_status_t pl061_gpio_config_in(void* ctx, uint32_t index, uint32_t flags) {
26     pl061_gpios_t* gpios = ctx;
27     index -= gpios->gpio_start;
28     volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE);
29     uint8_t bit = 1 << (index % GPIOS_PER_PAGE);
30 
31     mtx_lock(&gpios->lock);
32     uint8_t dir = readb(regs + GPIODIR);
33     dir &= ~bit;
34     writeb(dir, regs + GPIODIR);
35 
36 /* TODO(voydanoff) this should move to a gpio_get_interrupt callback
37     uint8_t trigger = readb(regs + GPIOIS);
38     if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_LEVEL) {
39         trigger |= bit;
40     } else {
41         trigger &= ~bit;
42     }
43     writeb(trigger, regs + GPIOIS);
44 
45     uint8_t be = readb(regs + GPIOIBE);
46     uint8_t iev = readb(regs + GPIOIEV);
47 
48     if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_EDGE && (flags & GPIO_TRIGGER_RISING)
49         && (flags & GPIO_TRIGGER_FALLING)) {
50         be |= bit;
51      } else {
52         be &= ~bit;
53      }
54     if ((flags & GPIO_TRIGGER_MASK) == GPIO_TRIGGER_EDGE && (flags & GPIO_TRIGGER_RISING)
55         && !(flags & GPIO_TRIGGER_FALLING)) {
56         iev |= bit;
57      } else {
58         iev &= ~bit;
59      }
60 
61     writeb(be, regs + GPIOIBE);
62     writeb(iev, regs + GPIOIEV);
63 */
64 
65 // TODO(voydanoff) Implement GPIO_PULL_* flags
66 
67     mtx_unlock(&gpios->lock);
68     return ZX_OK;
69 }
70 
pl061_gpio_config_out(void * ctx,uint32_t index,uint8_t initial_value)71 static zx_status_t pl061_gpio_config_out(void* ctx, uint32_t index, uint8_t initial_value) {
72     pl061_gpios_t* gpios = ctx;
73     index -= gpios->gpio_start;
74     volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE);
75     uint8_t bit = 1 << (index % GPIOS_PER_PAGE);
76 
77     mtx_lock(&gpios->lock);
78     // write value first
79     writeb((initial_value ? bit : 0), regs + GPIODATA(bit));
80 
81     // then set direction to OUT
82     uint8_t dir = readb(regs + GPIODIR);
83     dir |= bit;
84     writeb(dir, regs + GPIODIR);
85 
86     mtx_unlock(&gpios->lock);
87     return ZX_OK;
88 }
89 
pl061_gpio_set_alt_function(void * ctx,uint32_t index,uint64_t function)90 static zx_status_t pl061_gpio_set_alt_function(void* ctx, uint32_t index, uint64_t function) {
91     return ZX_ERR_NOT_SUPPORTED;
92 }
93 
pl061_gpio_read(void * ctx,uint32_t index,uint8_t * out_value)94 static zx_status_t pl061_gpio_read(void* ctx, uint32_t index, uint8_t* out_value) {
95     pl061_gpios_t* gpios = ctx;
96     index -= gpios->gpio_start;
97     volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE);
98     uint8_t bit = 1 << (index % GPIOS_PER_PAGE);
99 
100     *out_value = !!(readb(regs + GPIODATA(bit)) & bit);
101     return ZX_OK;
102 }
103 
pl061_gpio_write(void * ctx,uint32_t index,uint8_t value)104 static zx_status_t pl061_gpio_write(void* ctx, uint32_t index, uint8_t value) {
105     pl061_gpios_t* gpios = ctx;
106     index -= gpios->gpio_start;
107     volatile uint8_t* regs = gpios->buffer.vaddr + PAGE_SIZE * (index / GPIOS_PER_PAGE);
108     uint8_t bit = 1 << (index % GPIOS_PER_PAGE);
109 
110     writeb((value ? bit : 0), regs + GPIODATA(bit));
111     return ZX_OK;
112 }
113 
pl061_gpio_get_interrupt(void * ctx,uint32_t pin,uint32_t flags,zx_handle_t * out_handle)114 static zx_status_t pl061_gpio_get_interrupt(void* ctx, uint32_t pin, uint32_t flags,
115                                              zx_handle_t* out_handle) {
116     return ZX_ERR_NOT_SUPPORTED;
117 }
118 
pl061_gpio_release_interrupt(void * ctx,uint32_t pin)119 static zx_status_t pl061_gpio_release_interrupt(void* ctx, uint32_t pin) {
120     return ZX_ERR_NOT_SUPPORTED;
121 }
122 
pl061_gpio_set_polarity(void * ctx,uint32_t pin,uint32_t polarity)123 static zx_status_t pl061_gpio_set_polarity(void* ctx, uint32_t pin, uint32_t polarity) {
124     return ZX_ERR_NOT_SUPPORTED;
125 }
126 
127 gpio_impl_protocol_ops_t pl061_proto_ops = {
128     .config_in = pl061_gpio_config_in,
129     .config_out = pl061_gpio_config_out,
130     .set_alt_function = pl061_gpio_set_alt_function,
131     .read = pl061_gpio_read,
132     .write = pl061_gpio_write,
133     .get_interrupt = pl061_gpio_get_interrupt,
134     .release_interrupt = pl061_gpio_release_interrupt,
135     .set_polarity = pl061_gpio_set_polarity,
136 };
137