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