blob: 1d778d314f772e9a204cb5bb04e6a05ea94761d8 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fcntl.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <map>
#include <string>
#include <base/logging.h>
#include <hardware/hardware.h>
#include <hardware/peripheral_io.h>
// Path to sysfd gpio.
const char kSysfsGpioPathPrefix[] = "/sys/class/gpio/gpio";
// Path to export file.
const char kSysfsGpioExportPath[] = "/sys/class/gpio/export";
// Direction filename.
const char kDirection[] = "direction";
// Value filename.
const char kValue[] = "value";
static bool write_to_file(const std::string& path, const std::string& val) {
int fd = open(path.c_str(), O_WRONLY);
if (fd < 0)
return false;
ssize_t bytes = write(fd, val.c_str(), val.size());
close(fd);
if (bytes < 0)
return false;
if ((size_t)bytes != val.size())
return false;
return true;
}
static bool export_gpio(uint32_t index) {
std::string path = kSysfsGpioPathPrefix + std::to_string(index);
struct stat stat_buf;
if (!stat(path.c_str(), &stat_buf))
return true;
return write_to_file(kSysfsGpioExportPath, std::to_string(index));
}
static bool set_pin_direction(uint32_t index, const std::string& val) {
export_gpio(index);
std::string path =
kSysfsGpioPathPrefix + std::to_string(index) + "/direction";
return write_to_file(path, val);
}
static bool set_pin_val(uint32_t index, int val) {
export_gpio(index);
set_pin_direction(index, "out");
std::string path = kSysfsGpioPathPrefix + std::to_string(index) + "/value";
return write_to_file(path, std::to_string(val));
}
static bool set_output_buffer(uint32_t index, int val) {
export_gpio(index);
std::string path = kSysfsGpioPathPrefix + std::to_string(index) + "/value";
return write_to_file(path, std::to_string(val));
}
static bool set_mux_pin(uint32_t index, int val) {
set_pin_val(index, val);
return true;
}
static bool set_mode(uint32_t index, int val) {
export_gpio(index);
std::string path = kSysfsGpioPathPrefix + std::to_string(index) + "/pinmux";
return write_to_file(path, std::to_string(val));
}
struct EdisonPin {
uint32_t index;
uint32_t output_buffer;
};
std::map<std::string, EdisonPin> board_pins = {
{"IO0", {130, 248}}, {"IO1", {131, 249}}, {"IO2", {128, 250}},
{"IO3", {12, 251}}, {"IO4", {129, 252}}, {"IO5", {13, 253}},
{"IO6", {182, 254}}, {"IO7", {48, 255}}, {"IO8", {49, 256}},
{"IO9", {183, 257}}, {"IO10", {41, 258}}, {"IO11", {43, 259}},
{"IO12", {42, 260}}, {"IO13", {40, 261}}, {"IO14", {44, 232}},
{"IO15", {45, 233}}, {"IO16", {46, 234}}, {"IO17", {47, 235}},
{"IO18", {14, 236}}, {"IO19", {165, 237}},
};
static int pin_mux(const char* pin, const char* source) {
std::string pin_name(pin);
// If source is NULL, PIO is requesting GPIO.
if (!source) {
if (!board_pins.count(pin_name)) {
LOG(ERROR) << "PIO HAL: Unknown GPIO pin " << pin_name;
return false;
}
// Pin mode is always 0 for gpio.
set_mode(board_pins[pin_name].index, 0);
// Default to output buffer to input
set_output_buffer(board_pins[pin_name].output_buffer, 0);
// The following pins have an extra mux to set
if (pin_name == "IO10") {
set_mux_pin(263, 1);
set_mux_pin(240, 0);
set_mode(111, 0);
} else if (pin_name == "IO11") {
set_mux_pin(262, 1);
set_mux_pin(241, 0);
set_mode(115, 0);
} else if (pin_name == "IO12") {
set_mux_pin(242, 0);
set_mode(114, 0);
} else if (pin_name == "IO13") {
set_mux_pin(243, 0);
set_mode(109, 0);
} else if (pin_name == "IO14") {
set_mux_pin(200, 0);
} else if (pin_name == "IO15") {
set_mux_pin(201, 0);
} else if (pin_name == "IO16") {
set_mux_pin(202, 0);
} else if (pin_name == "IO17") {
set_mux_pin(203, 0);
} else if (pin_name == "IO18") {
set_mux_pin(204, 0);
} else if (pin_name == "IO19") {
set_mux_pin(205, 0);
}
return true;
}
std::string s = source;
// Configure SPI2
if (s == "SPI") {
if (pin_name == "IO10") {
set_pin_direction(226, "in");
set_mode(111, 1);
set_mux_pin(263, 1);
set_mux_pin(240, 1);
set_output_buffer(258, 1);
return true;
}
if (pin_name == "IO11") {
set_pin_direction(227, "in");
set_mode(115, 1);
set_mux_pin(262, 1);
set_mux_pin(241, 1);
set_output_buffer(259, 1);
return true;
}
if (pin_name == "IO12") {
set_pin_direction(228, "in");
set_output_buffer(260, 0);
set_mode(114, 1);
set_mux_pin(242, 1);
return true;
}
if (pin_name == "IO13") {
set_output_buffer(261, 0);
set_pin_direction(229, "in");
set_mode(109, 1);
set_mode(40, 1);
set_mux_pin(243, 1);
set_output_buffer(261, 1);
return true;
}
LOG(ERROR) << "PIO HAL: Unknown SPI pin " << pin_name;
return false;
}
// Configure I2C
if (s == "I2C") {
if (pin_name == "IO18") {
set_pin_direction(212, "in");
set_pin_direction(14, "in");
set_mode(28, 1);
set_mode(14, 1);
set_mux_pin(204, 0);
set_output_buffer(236, 0);
return true;
}
if (pin_name == "IO19") {
set_pin_direction(213, "in");
set_pin_direction(165, "in");
set_mode(165, 1);
set_mode(27, 1);
set_mux_pin(205, 0);
set_output_buffer(237, 0);
return true;
}
LOG(ERROR) << "PIO HAL: Unknown I2C pin " << pin_name;
}
if (s == "UART") {
if (pin_name == "IO0") {
set_pin_direction(248, "out");
set_pin_direction(216, "out");
set_output_buffer(248, 0);
set_output_buffer(216, 0);
set_mode(130, 1);
} else if(pin_name == "IO1") {
set_pin_direction(249, "out");
set_pin_direction(217, "in");
set_output_buffer(249, 1);
set_mode(131, 1);
}
}
return false;
}
static int pin_mux_direction(const char* pin, int dir) {
std::string pin_name(pin);
if (!board_pins.count(pin_name)) {
LOG(ERROR) << "PIO HAL: Unknown GPIO pin " << pin_name;
return false;
}
set_output_buffer(board_pins[pin_name].output_buffer, dir);
return true;
}
static int register_device(const peripheral_io_module_t* dev,
const peripheral_registration_cb_t* callbacks) {
LOG(INFO) << "Registering Edison's PIO HAL";
(void)dev;
// Set up pin muxing and register GPIO pins.
for (auto& pin : board_pins) {
callbacks->register_pin(pin.first.c_str(), true,
{pin_mux, pin_mux_direction});
callbacks->register_gpio_sysfs(pin.first.c_str(), pin.second.index);
callbacks->set_gpio_pin_mux(pin.first.c_str(), pin.first.c_str());
set_pin_direction(pin.second.output_buffer, "low");
}
const char* spi_pins[4] = {"IO10", "IO11", "IO12", "IO13"};
callbacks->register_simple_source("SPI", spi_pins, 4);
// Register the SPI bus
callbacks->register_spi_dev_bus("SPI2", 5, 1);
callbacks->set_spi_pin_mux("SPI2", "SPI");
const char* i2c_pins[2] = {"IO18", "IO19"};
callbacks->register_simple_source("I2C", i2c_pins, 2);
// Register the I2C bus
callbacks->register_i2c_dev_bus("I2C6", 6);
callbacks->set_i2c_pin_mux("I2C6", "I2C");
const char* uart_pins[2] = {"IO0", "IO1"};
callbacks->register_simple_source("UART", uart_pins, 2);
// TODO(leecam): Add UART back in once UART code lands
//callbacks->register_uart_bus("UART1", "/dev/ttyMFD1");
//callbacks->set_uart_pin_mux("UART1", "UART");
// Enable the Tri-State
set_pin_direction(214, "out");
set_pin_val(214, 1);
return 0;
}
static struct hw_module_methods_t hal_module_methods = {};
peripheral_io_module_t HAL_MODULE_INFO_SYM = {
.common =
{
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = PERIPHERAL_IO_HARDWARE_MODULE_ID,
.name = "periperal IO HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
.register_devices = register_device,
};