| /* |
| * 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, |
| }; |