/* * bus.c * * Created on: 9 Aug 2012 * Author: daniel */ #include #include #include #include #include #include #include #include #include #include #include "bus.h" #define GPIO_SYSFSPATH "/sys/class/gpio" #define GPIO_EXPORTNODE "export" #define GPIO_UNEXPORTNODE "unexport" #define GPIO_DIRECTIONNODE "direction" #define GPIO_VALUENODE "value" #define GPIO_LABELNODE "label" #define GPIO_BASENODE "base" #define GPIO_DIRECTION_IN "in" #define GPIO_DIRECTION_OUT "out" #define SIZEOFARRAY(a) (sizeof(a)/sizeof(a[0])) #define PATHLEN 256 #define GPMC_BASE 0x50000000 #define GPMC_REVISION 0x0 #define GPMC_SYSCONFIG (0x10 / 4) #define GPMC_SYSSTATUS (0x14 / 4) #define GPMC_IRQSTATUS (0x18 / 4) #define GPMC_IRQENABLE (0x1c / 4) #define GPMC_TIMEOUT_CONTROL (0x40 / 4) #define GPMC_ERR_ADDRESS (0x44 / 4) #define GPMC_ERR_TYPE (0x48 / 4) #define GPMC_CHIPSELECTCONFIGDISPLACEMENT (0x30 / 4) #define GPMC_CONFIG1 (0x60 / 4) #define GPMC_CONFIG2 (0x64 / 4) #define GPMC_CONFIG3 (0x68 / 4) #define GPMC_CONFIG4 (0x6c / 4) #define GPMC_CONFIG5 (0x70 / 4) #define GPMC_CONFIG6 (0x74 / 4) #define GPMC_CONFIG7 (0x78 / 4) #define GPMC_SIZE_256MB 0 #define GPMC_SIZE_128MB 0x8 #define GPMC_SIZE_64MB 0xc #define GPMC_SIZE_32MB 0xe #define GPMC_SIZE_16MB 0xf static int devmemfd = -1; static bool util_isbeaglebone() { int fd = open("/sys/kernel/debug/omap_mux/board/core", O_RDONLY); close(fd); return fd > -1; } static void* util_mapmemoryblock(off_t offset, size_t len) { devmemfd = open("/dev/mem", O_RDWR | O_SYNC); void* registers = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, devmemfd, offset); if (registers == MAP_FAILED) { printf("Map failed\n"); } return registers; } static void util_unmapmemoryblock(void* block, size_t len) { munmap((void*) block, len); if (devmemfd != -1) { close(devmemfd); } } static void pinmux_configurepin(char* pin, int options_and_mode) { char path[256]; snprintf(path, sizeof(path), "/sys/kernel/debug/omap_mux/%s", pin); FILE* node = fopen(path, "w"); if (node != NULL) { fprintf(node, "0x%02x\n", (options_and_mode & 0xff)); fclose(node); } else { printf("Failed to configure pin %s (%s)\n", pin, path); } } #define REGLEN 0x10000000 static volatile uint32_t* registers = NULL; static void gpmc_mapregisters() { registers = (uint32_t*) util_mapmemoryblock(GPMC_BASE, REGLEN); } static void gpmc_unmapregisters() { util_unmapmemoryblock((void*) registers, REGLEN); } static void gpmc_printconfig(int chipselect) { int displacement = GPMC_CHIPSELECTCONFIGDISPLACEMENT * chipselect; printf("Config for CS%d\n", chipselect); printf("CONFIG_1 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG1)); printf("CONFIG_2 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG2)); printf("CONFIG_3 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG3)); printf("CONFIG_4 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG4)); printf("CONFIG_5 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG5)); printf("CONFIG_6 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG6)); printf("CONFIG_7 [0x%08"PRIx32"]\n", *(registers + displacement + GPMC_CONFIG7)); } static void gpmc_printinfo() { gpmc_mapregisters(); uint32_t rev = *(registers + GPMC_REVISION); uint8_t major = (rev >> 4) & 0xF; uint8_t minor = rev & 0xF; printf("GPMC Rev is apparently %"PRId8".%"PRId8"\n", major, minor); printf("SYSCONFIG\t[0x%08"PRIx32"]\n", *(registers + GPMC_SYSCONFIG)); printf("SYSSTATUS\t[0x%08"PRIx32"]\n", *(registers + GPMC_SYSSTATUS)); printf("IRQSTATUS\t[0x%08"PRIx32"]\n", *(registers + GPMC_IRQSTATUS)); printf("IRQENABLE\t[0x%08"PRIx32"]\n", *(registers + GPMC_IRQENABLE)); printf("TIMEOUT_CONTROL\t[0x%08"PRIx32"]\n", *(registers + GPMC_TIMEOUT_CONTROL)); printf("ERR_ADDRESS\t[0x%08"PRIx32"]\n", *(registers + GPMC_ERR_ADDRESS)); printf("ERR_TYPE\t[0x%08"PRIx32"]\n", *(registers + GPMC_ERR_TYPE)); int i; for (i = 0; i < 8; i++) { gpmc_printconfig(i); } gpmc_unmapregisters(); } #define BIDIR 0x20 #define PULL_UP 0x10 #define PULL_DOWN 0x00 #define NO_PULL 0x80 #define MODE_0 0x00 #define MODE_1 0x01 #define MODE_2 0x02 #define MODE_3 0x03 #define MODE_4 0x04 #define MODE_5 0x05 #define MODE_6 0x06 #define MODE_7 0x07 // multiply by 20 ns #define TOTAL_IO_CYCLE 14 // 280 ns cycle #define CS_DELAY 1 // CS low 20ns after address valid #define DELAY_TO_WR_RD 2 // RD/WR lines low 40ns after address valid #define WR_RD_WIDTH 8 // stay low for until 200 ns after address valid #define DATA_READY 8 // data ready 160ns after address valid static void gpmc_setup(void) { int chipselect = 0; int size = GPMC_SIZE_16MB; bool enablecs = true; int baseaddress = 1; int CSWROFFTIME = TOTAL_IO_CYCLE; int CSRDOFFTIME = TOTAL_IO_CYCLE; int CSONTIME = CS_DELAY; int config2 = (CSWROFFTIME << 16) | (CSRDOFFTIME << 8) | CSONTIME; int WEOFFTIME = DELAY_TO_WR_RD + WR_RD_WIDTH; int WEONTIME = DELAY_TO_WR_RD; int OEOFFTIME = DELAY_TO_WR_RD + WR_RD_WIDTH; int OEONTIME = DELAY_TO_WR_RD; int config4 = (WEOFFTIME << 24) | (WEONTIME << 16) | (OEOFFTIME << 8) | OEONTIME; int RDACCESSTIME = DATA_READY; int WRCYCLETIME = TOTAL_IO_CYCLE; int RDCYCLETIME = TOTAL_IO_CYCLE; int config5 = (RDACCESSTIME << 16) | (WRCYCLETIME << 8) | RDCYCLETIME; int WRACCESSTIME = DATA_READY; int config6 = WRACCESSTIME << 24; //uart5 stuff pinmux_configurepin("lcd_data9", BIDIR | PULL_UP | MODE_4); pinmux_configurepin("lcd_data8", MODE_4); pinmux_configurepin("lcd_data14", BIDIR | PULL_UP | MODE_6); pinmux_configurepin("lcd_data15", MODE_6); // pinmux_configurepin("gpmc_csn0", PULL_UP | MODE_0); pinmux_configurepin("gpmc_oen_ren", PULL_UP | MODE_0); pinmux_configurepin("gpmc_wen", PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad7", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad6", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad5", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad4", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad3", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad2", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad1", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("gpmc_ad0", BIDIR | PULL_UP | MODE_0); pinmux_configurepin("lcd_data0", PULL_UP | MODE_1); pinmux_configurepin("lcd_data1", PULL_UP | MODE_1); pinmux_configurepin("lcd_data2", PULL_UP | MODE_1); pinmux_configurepin("lcd_data3", PULL_UP | MODE_1); pinmux_configurepin("lcd_data4", PULL_UP | MODE_1); pinmux_configurepin("lcd_data5", PULL_UP | MODE_1); pinmux_configurepin("lcd_data6", PULL_UP | MODE_1); pinmux_configurepin("lcd_data7", PULL_UP | MODE_1); int displacement = GPMC_CHIPSELECTCONFIGDISPLACEMENT * chipselect; gpmc_mapregisters(); if (registers != MAP_FAILED) { // disable before playing with the registers.. *(registers + displacement + GPMC_CONFIG7) = 0x0; *(registers + displacement + GPMC_CONFIG1) = 0x10; // double all of the timing values *(registers + displacement + GPMC_CONFIG2) = config2; *(registers + displacement + GPMC_CONFIG3) = 0x0; // not using ADV so we can ignore this guy *(registers + displacement + GPMC_CONFIG4) = config4; *(registers + displacement + GPMC_CONFIG5) = config5; *(registers + displacement + GPMC_CONFIG6) = config6; *(registers + displacement + GPMC_CONFIG7) = size << 8 | (enablecs ? 1 << 6 : 0) | baseaddress; gpmc_unmapregisters(); } } /* * Export a gpio from the kernel to userland */ static int gpio_export(unsigned gpio_pin) { FILE* exportfile = fopen(GPIO_SYSFSPATH"/"GPIO_EXPORTNODE, "w"); if (exportfile != NULL) { fprintf(exportfile, "%d\n", gpio_pin); fclose(exportfile); return 0; } return -1; } /* * Un-export a gpio from userland */ static int gpio_unexport(unsigned gpio_pin) { FILE* unexportfile = fopen(GPIO_SYSFSPATH"/"GPIO_UNEXPORTNODE, "w"); if (unexportfile != NULL) { fprintf(unexportfile, "%d\n", gpio_pin); fclose(unexportfile); return 0; } return -1; } /* * Change a pins direction */ static int gpio_changedirection(unsigned gpio_pin, bool out) { char path[PATHLEN]; snprintf(path, PATHLEN, GPIO_SYSFSPATH"/gpio%d/"GPIO_DIRECTIONNODE, gpio_pin); FILE* directionfile = fopen(path, "w"); if (directionfile != NULL) { if (out) { fputs(GPIO_DIRECTION_OUT, directionfile); } else { fputs(GPIO_DIRECTION_IN, directionfile); } fclose(directionfile); return 0; } return -1; } static void gpio_getvaluenodepath(unsigned gpio_pin, char* buffer) { snprintf(buffer, PATHLEN, GPIO_SYSFSPATH"/gpio%d/"GPIO_VALUENODE, gpio_pin); } /* * Read a pin's value */ static int gpio_readvalue(unsigned gpio_pin) { char path[PATHLEN]; gpio_getvaluenodepath(gpio_pin, path); FILE* valuefile = fopen(path, "r"); if (valuefile != NULL) { char value[2]; // value will be a single char and \n fread(value, 1, 2, valuefile); fclose(valuefile); return atoi(value); } return -1; } /* * Write a pin's value */ static int gpio_writevalue(unsigned gpio_pin, int value) { char path[PATHLEN]; gpio_getvaluenodepath(gpio_pin, path); FILE* valuefile = fopen(path, "w"); if (valuefile != NULL) { fprintf(valuefile, "%d\n", value); fclose(valuefile); return 0; } return -1; } // gpio pins are grouped into 4 bunches of 32 pins #define GPIO0_X (0 * 32) #define GPIO1_X (1 * 32) #define GPIO2_X (2 * 32) #define GPIO3_X (3 * 32) static unsigned gpio_pins[] = { GPIO0_X + 22, // i.e., GPIO0_22 GPIO0_X + 23, GPIO0_X + 26, GPIO1_X + 15, // i.e., GPIO1_15 GPIO1_X + 14, GPIO0_X + 27, GPIO1_X + 12 }; static volatile uint8_t* extbus; static bool isbb = false; void bus_init() { isbb = util_isbeaglebone(); if (!isbb) { printf("This doesn't seem to be a beaglebone.. bus stuff disabled!\n"); } if (isbb) { gpmc_setup(); extbus = (uint8_t*) util_mapmemoryblock(0x01000000, 0x100); //gpmc_printinfo(); int i; for (i = 0; i < SIZEOFARRAY(gpio_pins); i++) { // these pins all happen to default to the correct mode (7) // so no call to pinmux_configurepin is necessary gpio_export(gpio_pins[i]); gpio_changedirection(gpio_pins[i], true); } // FIXME: make this more elegant // // the POWER_FAIL pin is an input gpio_changedirection(gpio_pins[POWER_FAIL], false); } } int bus_getpin(int pin) { if (isbb) { return gpio_readvalue(gpio_pins[pin]); } else { return 0; } } void bus_setpin(int pin, int value) { if (isbb) { gpio_writevalue(gpio_pins[pin], value & 0x1); } } void bus_writebyte(uint8_t address, uint8_t data) { if (isbb) { *(extbus + address) = data; } } uint8_t bus_readbyte(uint8_t address) { if (isbb) { return *(extbus + address); } else { return 0; } } void bus_shutdown() { if (isbb) { util_unmapmemoryblock((void*) extbus, 0x100); int i; for (i = 0; i < SIZEOFARRAY(gpio_pins); i++) { gpio_unexport(gpio_pins[i]); } } }