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