11
#include <drivers/common/memory.h>
12
#include <embox/unit.h>
13
#include <lib/libds/array.h>
14
#include <kernel/printk.h>
15
#include <kernel/panic.h>
16
#include <kernel/irq.h>
18
#include <framework/mod/options.h>
19
#include "bcm283x_gpio.h"
21
#include <drivers/gpio/gpio_driver.h>
25
#define PBASE OPTION_GET(NUMBER,base_addr)
26
#define BCM283X_PINS_NUMBER OPTION_GET(NUMBER,gpio_pins)
27
#define BCM283X_GPIO_PORTS_COUNT OPTION_GET(NUMBER,gpio_ports)
28
#define GPU_IRQ OPTION_GET(NUMBER,gpu_irq_int)
30
#define BCM283X_PULL_PORTA_DEFLT_FLOAT OPTION_GET(NUMBER,gpu_pullup_porta_float)
31
#define BCM283X_PULL_PORTA_DEFLT_DOWN OPTION_GET(NUMBER,gpu_pullup_porta_down)
32
#define BCM283X_PULL_PORTA_DEFLT_UP OPTION_GET(NUMBER,gpu_pullup_porta_up)
34
#define BCM283X_PULL_PORTB_DEFLT_FLOAT OPTION_GET(NUMBER,gpu_pullup_portb_float)
35
#define BCM283X_PULL_PORTB_DEFLT_DOWN OPTION_GET(NUMBER,gpu_pullup_portb_down)
36
#define BCM283X_PULL_PORTB_DEFLT_UP OPTION_GET(NUMBER,gpu_pullup_portb_up)
38
#define PORT_PIN_TO_PINNUMBER(pt, pn) ( ( ((pt) & 0x3 ) * REG_BITS ) + ((pn) % REG_BITS ))
39
#define GPIO_ALTERNATE_INVERT(af) ( ((af) >> 17) & 0x07 )
40
#define BCM283X_GPIO_CHIP_ID 0
42
#define IRQ_NR_PORTA 49
43
#define IRQ_NR_PORTB 50
44
#define IRQ_NR_PORTC 51
47
typedef enum _GpioPullUpDn {
53
#define GPIO_PULL_NONE_UP_DN_STATES 3
60
volatile uint32_t reserved;
61
volatile uint32_t data[BCM283X_GPIO_PORTS_COUNT];
65
volatile uint32_t func_select[3 * BCM283X_GPIO_PORTS_COUNT];
66
struct GpioPinData output_set;
67
struct GpioPinData output_clear;
68
struct GpioPinData level;
69
struct GpioPinData ev_detect_status;
70
struct GpioPinData re_detect_enable;
71
struct GpioPinData fe_detect_enable;
72
struct GpioPinData hi_detect_enable;
73
struct GpioPinData lo_detect_enable;
74
struct GpioPinData re_async_detect_enable;
76
struct GpioPinData fe_async_detect_enable;
77
volatile uint32_t reserved;
78
volatile uint32_t pupd_enable;
79
volatile uint32_t pupd_enable_clocks[BCM283X_GPIO_PORTS_COUNT];
82
#define REGS_GPIO ((struct GpioRegs *)(PBASE))
86
static uint32_t _pinMaskPortA[GPIO_PULL_NONE_UP_DN_STATES];
87
static uint32_t _pinMaskPortB[GPIO_PULL_NONE_UP_DN_STATES];
92
static void bcm293x_gpio_assign_pullup(GpioPullUpDn pull, unsigned char port, gpio_mask_t pin) {
93
uint32_t *pullPorts[BCM283X_GPIO_PORTS_COUNT] = {_pinMaskPortA, _pinMaskPortB};
94
GpioPullUpDn stateSelection[GPIO_PULL_NONE_UP_DN_STATES];
97
stateSelection[0] = GPUDUp;
98
stateSelection[1] = GPUDDown;
99
stateSelection[2] = GPUDNone;
102
stateSelection[0] = GPUDDown;
103
stateSelection[1] = GPUDNone;
104
stateSelection[2] = GPUDUp;
107
stateSelection[0] = GPUDNone;
108
stateSelection[1] = GPUDUp;
109
stateSelection[2] = GPUDDown;
112
pullPorts[port][stateSelection[0]] |= (1 << pin);
113
pullPorts[port][stateSelection[1]] &= ~(1 << pin);
114
pullPorts[port][stateSelection[2]] &= ~(1 << pin);
120
static void bcm283x_gpio_pin_set_func(unsigned char port, gpio_mask_t pins, GpioFunc func) {
121
uint32_t pinNumber, selector;
122
uint8_t bitStart, reg;
124
for(unsigned short r = 0; r < REG_BITS; r++ ) {
125
if( (pins >> r) & 0x01 ) {
126
pinNumber = PORT_PIN_TO_PINNUMBER(port, r);
127
bitStart = (pinNumber * 3) % 30;
128
reg = pinNumber / 10;
130
selector = REGS_GPIO->func_select[reg];
131
selector &= ~(7 << bitStart);
132
selector |= (func << bitStart);
134
REGS_GPIO->func_select[reg] = selector;
141
static void bcm283x_gpio_pin_pullupdown(GpioPullUpDn pullState, uint32_t pinMaskPortA, uint32_t pinMaskPortB) {
143
REGS_GPIO->pupd_enable = pullState;
146
REGS_GPIO->pupd_enable_clocks[GPIO_PORT_A] = pinMaskPortA;
147
REGS_GPIO->pupd_enable_clocks[GPIO_PORT_B] = pinMaskPortB;
150
REGS_GPIO->pupd_enable = 0;
151
REGS_GPIO->pupd_enable_clocks[GPIO_PORT_A] = 0;
152
REGS_GPIO->pupd_enable_clocks[GPIO_PORT_B] = 0;
160
static int bcm283x_gpio_setup_mode(unsigned char port, gpio_mask_t pins, int mode) {
161
assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
164
if(mode & GPIO_MODE_OUT_ALTERNATE) {
165
bcm283x_gpio_pin_set_func(port,pins,GPIO_ALTERNATE_INVERT(mode));
166
} else if (mode & (GPIO_MODE_IN
167
| GPIO_MODE_IN_PULL_UP | GPIO_MODE_IN_PULL_DOWN
168
| GPIO_MODE_IN_SCHMITT
169
| GPIO_MODE_INT_MODE_LEVEL0 | GPIO_MODE_INT_MODE_LEVEL1
170
| GPIO_MODE_INT_MODE_RISING | GPIO_MODE_INT_MODE_FALLING )) {
171
bcm283x_gpio_pin_set_func(port,pins,GFInput);
173
bcm283x_gpio_pin_set_func(port,pins,GFOutput);
177
if( mode & GPIO_MODE_INT_MODE_RISING ) {
178
REGS_GPIO->re_detect_enable.data[port] |= pins;
179
REGS_GPIO->ev_detect_status.data[port] |= pins;
181
REGS_GPIO->re_detect_enable.data[port] &= ~pins;
185
if( mode & GPIO_MODE_INT_MODE_FALLING ) {
186
REGS_GPIO->fe_detect_enable.data[port] |= pins;
187
REGS_GPIO->ev_detect_status.data[port] |= pins;
189
REGS_GPIO->fe_detect_enable.data[port] &= ~pins;
193
if( mode & GPIO_MODE_INT_MODE_LEVEL0 ) {
194
REGS_GPIO->lo_detect_enable.data[port] |= pins;
195
REGS_GPIO->ev_detect_status.data[port] |= pins;
197
REGS_GPIO->lo_detect_enable.data[port] &= ~pins;
201
if( mode & GPIO_MODE_INT_MODE_LEVEL1 ) {
202
REGS_GPIO->hi_detect_enable.data[port] |= pins;
203
REGS_GPIO->ev_detect_status.data[port] |= pins;
205
REGS_GPIO->hi_detect_enable.data[port] &= ~pins;
210
if(mode & GPIO_MODE_IN_SCHMITT && mode & GPIO_MODE_INT_MODE_RISING) {
211
REGS_GPIO->re_async_detect_enable.data[port] &= ~pins;
213
REGS_GPIO->re_async_detect_enable.data[port] |= pins;
218
if(mode & GPIO_MODE_IN_SCHMITT && mode & GPIO_MODE_INT_MODE_FALLING) {
219
REGS_GPIO->fe_async_detect_enable.data[port] &= ~pins;
221
REGS_GPIO->fe_async_detect_enable.data[port] |= pins;
226
if(!( mode & GPIO_MODE_INT_MODE_LEVEL1
227
|| mode & GPIO_MODE_INT_MODE_LEVEL0
228
|| mode & GPIO_MODE_INT_MODE_RISING
229
|| mode & GPIO_MODE_INT_MODE_FALLING)) {
230
REGS_GPIO->ev_detect_status.data[port] &= ~pins;
231
REGS_GPIO->fe_async_detect_enable.data[port] &= ~pins;
232
REGS_GPIO->re_async_detect_enable.data[port] &= ~pins;
236
if(mode & (GPIO_MODE_IN_PULL_UP | GPIO_MODE_OUT_PUSH_PULL) ) {
237
bcm293x_gpio_assign_pullup(GPUDUp, port, pins);
239
if(mode & (GPIO_MODE_IN_PULL_DOWN | GPIO_MODE_VDD_LEVEL) ) {
240
bcm293x_gpio_assign_pullup(GPUDDown, port, pins);
244
for( GpioPullUpDn i = 0; i < GPIO_PULL_NONE_UP_DN_STATES; i++) {
245
bcm283x_gpio_pin_pullupdown(i, _pinMaskPortA[i], _pinMaskPortB[i]);
251
static void bcm283x_gpio_set(unsigned char port, gpio_mask_t pins, char level) {
252
assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
255
REGS_GPIO->output_set.data[port] |= pins;
257
REGS_GPIO->output_clear.data[port] |= pins;
261
static gpio_mask_t bcm283x_gpio_get(unsigned char port, gpio_mask_t pins) {
262
assert(((port == GPIO_PORT_A) || (port == GPIO_PORT_B)));
264
return REGS_GPIO->level.data[port] & pins;
267
static struct gpio_chip bcm283x_gpio_chip = {
268
.setup_mode = bcm283x_gpio_setup_mode,
269
.get = bcm283x_gpio_get,
270
.set = bcm283x_gpio_set,
271
.nports = BCM283X_GPIO_PORTS_COUNT
274
irq_return_t bcm283x_gpio_irq_handler(unsigned int irq_nr, void *data) {
275
uint16_t ports[BCM283X_GPIO_PORTS_COUNT] = {0,0};
277
ports[0] = (IRQ_NR_PORTA == irq_nr || IRQ_NR_ALL == irq_nr );
278
ports[1] = (IRQ_NR_PORTB == irq_nr || IRQ_NR_ALL == irq_nr );
280
for(uint16_t port = 0; port < BCM283X_GPIO_PORTS_COUNT; port++) {
282
uint32_t pins = REGS_GPIO->ev_detect_status.data[port];
285
REGS_GPIO->ev_detect_status.data[port] = 0xFFFFFFFF;
287
gpio_handle_irq(&bcm283x_gpio_chip, port, pins);
294
static int bcm283x_gpio_init(void) {
301
uint32_t a[GPIO_PULL_NONE_UP_DN_STATES] = {BCM283X_PULL_PORTA_DEFLT_FLOAT, BCM283X_PULL_PORTA_DEFLT_DOWN, BCM283X_PULL_PORTA_DEFLT_UP};
302
uint32_t b[GPIO_PULL_NONE_UP_DN_STATES] = {BCM283X_PULL_PORTB_DEFLT_FLOAT, BCM283X_PULL_PORTB_DEFLT_DOWN, BCM283X_PULL_PORTB_DEFLT_UP};
304
for( GpioPullUpDn i = GPUDNone; i < GPIO_PULL_NONE_UP_DN_STATES; i++ ) {
305
_pinMaskPortA[i] = a[i];
306
_pinMaskPortB[i] = b[i];
309
res = irq_attach(GPU_IRQ, bcm283x_gpio_irq_handler, 0, NULL, "BCM283x GPIO irq handler");
311
res = gpio_register_chip(&bcm283x_gpio_chip, BCM283X_GPIO_CHIP_ID);
316
EMBOX_UNIT_INIT(bcm283x_gpio_init);
318
PERIPH_MEMORY_DEFINE(bcm283x_gpio, PBASE, sizeof(struct GpioRegs));