diff options
Diffstat (limited to 'recipes/linux/linux-2.6.33/adb4000/linux-2.6.33.2-0016-ADB4000-Adding-support-for-the-IO-Processor.patch')
-rw-r--r-- | recipes/linux/linux-2.6.33/adb4000/linux-2.6.33.2-0016-ADB4000-Adding-support-for-the-IO-Processor.patch | 1785 |
1 files changed, 1785 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.33/adb4000/linux-2.6.33.2-0016-ADB4000-Adding-support-for-the-IO-Processor.patch b/recipes/linux/linux-2.6.33/adb4000/linux-2.6.33.2-0016-ADB4000-Adding-support-for-the-IO-Processor.patch new file mode 100644 index 0000000000..cd63f9eb99 --- /dev/null +++ b/recipes/linux/linux-2.6.33/adb4000/linux-2.6.33.2-0016-ADB4000-Adding-support-for-the-IO-Processor.patch @@ -0,0 +1,1785 @@ +From babb2d6e7ec94f3ff102298a34862baf47eb466f Mon Sep 17 00:00:00 2001 +From: Benjamin Tietz <benjamin.tietz@in-circuit.de> +Date: Thu, 16 Dec 2010 13:53:13 +0100 +Subject: [PATCH 16/18] [ADB4000] Adding support for the IO-Processor + +The ICnova ADB4000 is equipped with an IO-Processor, adding more IOs, +analog inputs and a buzzer. +--- + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/adb4000/Kconfig | 86 +++ + drivers/misc/adb4000/Makefile | 2 + + drivers/misc/adb4000/spi_comm.c | 107 +++ + drivers/misc/adb4000/spi_comm.h | 49 ++ + drivers/misc/adb4000/supervisor.c | 1411 +++++++++++++++++++++++++++++++++++++ + drivers/misc/adb4000/supervisor.h | 46 ++ + 8 files changed, 1703 insertions(+), 0 deletions(-) + create mode 100644 drivers/misc/adb4000/Kconfig + create mode 100644 drivers/misc/adb4000/Makefile + create mode 100644 drivers/misc/adb4000/spi_comm.c + create mode 100644 drivers/misc/adb4000/spi_comm.h + create mode 100644 drivers/misc/adb4000/supervisor.c + create mode 100644 drivers/misc/adb4000/supervisor.h + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 8d0a517..c8aa1e2 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -296,6 +296,7 @@ source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" + source "drivers/misc/iwmc3200top/Kconfig" ++source "drivers/misc/adb4000/Kconfig" + + config FPGA_SRAM + tristate "FPGA-SRAM Interface" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 61fe337..1b1e4f4 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -29,3 +29,4 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ + obj-$(CONFIG_FPGA_SRAM) += fpga_sram.o + obj-y += eeprom/ + obj-y += cb710/ ++obj-y += adb4000/ +diff --git a/drivers/misc/adb4000/Kconfig b/drivers/misc/adb4000/Kconfig +new file mode 100644 +index 0000000..945b643 +--- /dev/null ++++ b/drivers/misc/adb4000/Kconfig +@@ -0,0 +1,86 @@ ++config SPI_COMM ++ tristate ++ default n ++ help ++ Communication-layer for supervisor ++ ++config SUPERVISOR ++ depends on SPI ++ select SPI_COMM ++ tristate "ADB4000 Supervisor" ++ default y ++ help ++ The supervisor expands the board by a couple of UART-lines as ++ well as some buttons and GPIOs. ++ ++config SUPERVISOR_ATOI ++ bool ++ default n ++ help ++ Provide a simple str2int function ++ ++config SUPERVISOR_LED ++ depends on SUPERVISOR ++ bool "Support LEDs on Supervisor" ++ default y ++ help ++ This will utilize the Supervisors LEDs as those and not as GPIO. ++ ++config SUPERVISOR_IRQ ++ depends on SUPERVISOR ++ bool "Support for Interrupt driven GPIOs on Supervisor" ++ default y ++ help ++ This will make the GPIOs on the Supervisor be able to trigger ++ interrupts on the system. ++ ++config SUPERVISOR_UART ++ depends on SUPERVISOR ++ bool "Make UARTs available" ++ default y ++ help ++ The Supervisor can provide a set of UART, connected to some ++ Radios or utilized for RS232/RS422/RS485 ++ ++config SUPERVISOR_SYSFS ++ depends on SUPERVISOR ++ bool ++ default n ++ ++config SUPERVISOR_BUZZER ++ depends on SUPERVISOR ++ select SUPERVISOR_SYSFS ++ select SUPERVISOR_ATOI ++ bool "Give access to the buzzer" ++ default y ++ help ++ This will give access to the supervisor's buzzer via a sysfs-file ++ ++config SUPERVISOR_ADC ++ depends on SUPERVISOR ++ select SUPERVISOR_SYSFS ++ select SUPERVISOR_ATOI ++ bool "Utilize the Analog input Streams" ++ default y ++ help ++ To use the analog inputs and switch them from 0-10V to 4-20mA and ++ vice versa, this will be needed. ++ ++config SUPERVISOR_CNTIN ++ depends on SUPERVISOR ++ select SUPERVISOR_SYSFS ++ select SUPERVISOR_ATOI ++ bool "Use digital inputs as counter" ++ default y ++ ++config SUPERVISOR_NOCHECK ++ depends on SUPERVISOR ++ bool "Do not test for the presence of the Supervisor" ++ default n ++ ++config SUPERVISOR_BOOTLOADER ++ depends on SUPERVISOR ++ select SUPERVISOR_SYSFS ++ bool "Allow Firmwareupgrades to the Supervisor" ++ default y ++ +diff --git a/drivers/misc/adb4000/Makefile b/drivers/misc/adb4000/Makefile +new file mode 100644 +index 0000000..7857f0a +--- /dev/null ++++ b/drivers/misc/adb4000/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_SPI_COMM) += spi_comm.o ++obj-$(CONFIG_SUPERVISOR)+= supervisor.o +diff --git a/drivers/misc/adb4000/spi_comm.c b/drivers/misc/adb4000/spi_comm.c +new file mode 100644 +index 0000000..daed9c9 +--- /dev/null ++++ b/drivers/misc/adb4000/spi_comm.c +@@ -0,0 +1,107 @@ ++#define DEBUG ++#include "spi_comm.h" ++#include <linux/crc32.h> ++ ++#define CRC32_SEED 0x04C11DB7UL ++ ++static int spi_comm_resend(struct spi_comm *spi) { ++ return spi_async(spi->spi, &spi->msg); ++} ++ ++static int spi_comm_recv(struct spi_comm *spi, char buf[XM_SPI_BUF_SIZE]) { ++ u32 crc, crcr; ++ crc = crc32(CRC32_SEED, spi->rx+1, XM_SPI_BUF_SIZE); ++ pr_debug("Got %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ spi->rx[1], spi->rx[2], spi->rx[3], spi->rx[4], ++ spi->rx[5], spi->rx[6], spi->rx[7], spi->rx[8], ++ spi->rx[9], spi->rx[10], spi->rx[11], spi->rx[12]); ++ memcpy(&crcr, spi->rx + XM_SPI_BUF_SIZE+1, sizeof(crc)); ++ pr_debug("CRC %08X vs %08X\n", crc, crcr); ++ if(crc == crcr) { ++ memcpy(buf, spi->rx, XM_SPI_BUF_SIZE); ++ return 1; ++ } ++ return 0; ++} ++ ++static inline void spi_comm_cb(struct spi_comm *spi, char buf[XM_SPI_BUF_SIZE]) { ++ if(spi->cb == NULL) return; ++ if(spi->cb(buf, spi) >= 0) return; ++ if(spi->pckretry == 0) { ++ dev_warn(&spi->spi->dev, "Msg timed out\n"); ++ return; ++ } ++ spi->pckretry--; ++ spi_comm_resend(spi); ++} ++ ++static void spi_comm_complete(void *ctx) { ++ struct spi_comm *spi = ctx; ++ char buf[XM_SPI_BUF_SIZE]; ++ ++ if(spi->msg.status) { ++ spi->state = SPIC_ERR; ++ spi_comm_cb(spi, NULL); ++ return; ++ } ++ if(!spi_comm_recv(spi, buf)) { ++ if(spi->pckretry) { ++ spi->pckretry--; ++ spi_comm_resend(spi); ++ return; ++ } ++ spi->state = SPIC_CRCERR; ++ spi_comm_cb(spi, NULL); ++ return; ++ } ++ spi_comm_cb(spi, spi->rx + 1); ++ return; ++} ++ ++int spi_comm_msg(struct spi_comm *spi, char buf[XM_SPI_BUF_SIZE]) { ++ u32 crc; ++ ++ pr_debug("Msg %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ buf[0], buf[1], buf[2], buf[3], ++ buf[4], buf[5], buf[6], buf[7], ++ buf[8], buf[9], buf[10], buf[11]); ++ memcpy(spi->tx, buf, XM_SPI_BUF_SIZE); ++ crc = crc32(CRC32_SEED, buf, XM_SPI_BUF_SIZE); ++ pr_debug("CRC 0x%08x size %i+%i\n", crc, XM_SPI_BUF_SIZE, sizeof(crc)); ++ memcpy(spi->tx + XM_SPI_BUF_SIZE, &crc, sizeof(crc)); ++ spi->trans.tx_buf = spi->tx; ++ spi->trans.rx_buf = spi->rx; ++ spi->trans.len = sizeof(spi->tx); ++ spi_message_init(&spi->msg); ++ spi->msg.spi = spi->spi; ++ spi->msg.complete = spi_comm_complete; ++ spi->msg.context = spi; ++ spi->pckretry = spi->retries; ++ spi_message_add_tail(&spi->trans, &spi->msg); ++ return spi_comm_resend(spi); ++} ++ ++int spi_comm_init(struct spi_comm *spic, struct spi_device *spi, int retries, ++ int busypin) { ++ spic->spi = spi; ++ spic->retries = retries; ++ spic->pckretry = retries; ++ return 0; ++} ++ ++void spi_comm_destroy(struct spi_comm *spic) { ++ return; ++} ++ ++int spi_comm_busy(struct spi_comm *spic) { ++ return 0; ++} ++ ++EXPORT_SYMBOL(spi_comm_msg); ++EXPORT_SYMBOL(spi_comm_destroy); ++EXPORT_SYMBOL(spi_comm_init); ++EXPORT_SYMBOL(spi_comm_busy); ++ ++MODULE_AUTHOR("Benjamin Tietz <benjamin.tietz@in-circuit.de>"); ++MODULE_DESCRIPTION("XMEGA-SPI Communication Protocol"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/adb4000/spi_comm.h b/drivers/misc/adb4000/spi_comm.h +new file mode 100644 +index 0000000..a9e3619 +--- /dev/null ++++ b/drivers/misc/adb4000/spi_comm.h +@@ -0,0 +1,49 @@ ++#ifndef __SPI_COMM_H ++#define __SPI_COMM_H ++ ++#include <linux/spi/spi.h> ++#include <linux/wait.h> ++ ++#define XM_SPI_BUF_SIZE 12 ++//#define XM_SPI_BUF_SIZE 28 ++#define CRC32_SEED 0x04C11DB7UL ++ ++struct spi_comm; ++struct spi_comm { ++ /* These fields must be initialized by the driver */ ++ struct spi_device *spi; ++ int (*cb)(char buf[XM_SPI_BUF_SIZE], struct spi_comm *); ++ int retries; ++ /* This imposes the actual state of the received msg */ ++ enum { ++ SPIC_OK, ++ SPIC_CRCERR, ++ SPIC_ERR, ++ } state; ++ int msg_state; ++ /* These should be ignored and handled internally */ ++#ifdef CONFIG_SPI_COMM_V2 ++ int busypin, busyirq, sending; ++ wait_queue_head_t busywaiter; ++#else ++ int pckretry; ++ struct spi_message msg; ++ struct spi_transfer trans; ++ char rx[XM_SPI_BUF_SIZE + 5], tx[XM_SPI_BUF_SIZE + 5]; ++#endif ++}; ++ ++/* ++ * This intializes and set up a spi_comm struct. ++ */ ++int spi_comm_init(struct spi_comm *spic, struct spi_device *spi, int retries, ++ int busypin); ++void spi_comm_destroy(struct spi_comm *spic); ++ ++int spi_comm_busy(struct spi_comm *spic); ++/* ++ * This set up a new message and queues it to be send ++ */ ++int spi_comm_msg(struct spi_comm *spi, char buf[XM_SPI_BUF_SIZE]); ++ ++#endif +diff --git a/drivers/misc/adb4000/supervisor.c b/drivers/misc/adb4000/supervisor.c +new file mode 100644 +index 0000000..f5c3b2a +--- /dev/null ++++ b/drivers/misc/adb4000/supervisor.c +@@ -0,0 +1,1411 @@ ++/* ++ * This is the kernel-driver for the ADB4000 Supervisor ++ * ++ * (C) 2010 by Benjamin Tietz <benjamin.tietz@in-circuit.de> ++ */ ++ ++//#define DEBUG ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/timer.h> ++#include <linux/wait.h> ++#include <linux/sched.h> ++#include <linux/leds.h> ++#include <linux/platform_device.h> ++#ifdef CONFIG_SUPERVISOR_IRQ ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#endif ++#ifdef CONFIG_SUPERVISOR_UART ++#include <linux/serial_core.h> ++#endif ++#ifdef CONFIG_SUPERVISOR_SYSFS ++#include <linux/hwmon-sysfs.h> ++#endif ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++#include <linux/firmware.h> ++#include <linux/crc32.h> ++#endif ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include "spi_comm.h" ++#include "supervisor.h" ++ ++#define UART_NAME "ttyU" ++#define UART_MAJOR 204 ++#define UART_MINOR 160 ++#define UART_NR 4 ++#define PORT_UARTEXTENDER 0xCBC ++#define UART_BUFSIZE 1024 ++ ++#define SUPERVISOR_SENDTO 20 ++ ++#define SUP_ADC_NUM 2 ++#define SPI_VERSION_STRING "ICURTGP1.1" ++#define SPI_BL_VERSION_STRING "ICBOOTL0.1" ++ ++typedef uint32_t counter_t; ++ ++struct supervisor_data; ++#ifdef CONFIG_SUPERVISOR_UART ++struct uartextender_port { ++ struct uart_port uart; ++ struct supervisor_data *data; ++ int enabled:1, rx:1, tx:1; ++}; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_ADC ++enum supervisor_adc_type { ++ SUP_ADC_0_10_V, ++ SUP_ADC_4_20_mA, ++}; ++#endif ++ ++struct supervisor_data { ++ struct spi_comm spic; ++ struct gpio_chip gpioc; ++ spinlock_t lock; ++ ++ /* transmission control */ ++ char rx[XM_SPI_BUF_SIZE/3][3], tx[XM_SPI_BUF_SIZE/3][3]; ++ wait_queue_head_t recvq; ++ int rx_valid:1, last_valid:1; ++ struct timer_list recvtimer, sendtimer; ++ ++#ifdef CONFIG_SUPERVISOR_LED ++ /* LEDs */ ++ struct gpio_led leds[4]; ++ struct gpio_led_platform_data leds_plat; ++ struct platform_device leds_dev; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_IRQ ++ /* Interrupt support */ ++ int firq; ++#endif ++#if defined(CONFIG_SUPERVISOR_UART) || defined(CONFIG_SUPERVISOR_IRQ) ++ int hirq; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_UART ++ struct timer_list ussendtimer; ++ struct timer_list testtimer; ++ struct uartextender_port port[UART_NR]; ++ int ports_enabled; ++#endif ++#ifdef CONFIG_SUPERVISOR_BUZZER ++#endif ++#ifdef CONFIG_SUPERVISOR_ADC ++ enum supervisor_adc_type sensortype[SUP_ADC_NUM]; ++#endif ++#ifndef CONFIG_SUPERVISOR_NOCHECK ++ int found:1; ++#endif ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ int bootloader:1; ++ enum { ++ BL_INIT = 0, ++ BL_WAIT_FW, ++ BL_CONTACTING, ++ BL_LOADING, ++ BL_VERIFYING, ++ BL_DONE, ++ BL_ERR, ++ } blstate; ++ wait_queue_head_t blw; ++ struct workqueue_struct *blwq; ++ struct work_struct blwork; ++ char blbuf[XM_SPI_BUF_SIZE]; ++#endif ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ int cnt_valid:1, cnt_tx_sent:1; ++ char cnt_buf[sizeof(counter_t)]; ++ char cnt_tx_buf[XM_SPI_BUF_SIZE]; ++#endif ++}; ++ ++#ifdef CONFIG_SUPERVISOR_BUZZER ++static ssize_t sp_buzzer(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len); ++#endif ++#ifdef CONFIG_SUPERVISOR_ADC ++static ssize_t sen_val(struct device *dev, ++ struct device_attribute *dattr, char *buf); ++static ssize_t sen_get_type(struct device *dev, ++ struct device_attribute *dattr, char *buf); ++static ssize_t sen_set_type(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len); ++static ssize_t sen_get_cal(struct device *dev, ++ struct device_attribute *dattr, char *buf); ++static ssize_t sen_set_cal(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len); ++ ++#define ADCCALVMIN (0<<8) ++#define ADCCALVMAX (1<<8) ++#define ADCCALCMIN (2<<8) ++#define ADCCALCMAX (3<<8) ++#endif ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++static ssize_t bl_start_load(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len); ++static ssize_t bl_get_state(struct device *dev, ++ struct device_attribute *dattr, char *buf); ++#endif ++#ifdef CONFIG_SUPERVISOR_CNTIN ++static ssize_t cnt_set(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len); ++static ssize_t cnt_get(struct device *dev, ++ struct device_attribute *dattr, char *buf); ++#endif ++ ++ ++#ifdef CONFIG_SUPERVISOR_SYSFS ++static struct sensor_device_attribute sp_sensors[] = { ++#ifdef CONFIG_SUPERVISOR_BUZZER ++ SENSOR_ATTR(buzzer, 0222, NULL, sp_buzzer, GE_BUZZER), ++#endif ++#ifdef CONFIG_SUPERVISOR_ADC ++ SENSOR_ATTR(adc0raw, 0444, sen_val, NULL, GE_ADC(0)), ++ SENSOR_ATTR(adc1raw, 0444, sen_val, NULL, GE_ADC(1)), ++ SENSOR_ATTR(adc0type, 0666, sen_get_type, sen_set_type, 0), ++ SENSOR_ATTR(adc1type, 0666, sen_get_type, sen_set_type, 1), ++ SENSOR_ATTR(adc0value, 0444, sen_val, NULL, GE_ADCCALIB(0)), ++ SENSOR_ATTR(adc1value, 0444, sen_val, NULL, GE_ADCCALIB(1)), ++ SENSOR_ATTR(adc0calibVmax, 0644, ++ sen_get_cal, sen_set_cal, ADCCALVMAX | 0), ++ SENSOR_ATTR(adc1calibVmax, 0644, ++ sen_get_cal, sen_set_cal, ADCCALVMAX | 1), ++ SENSOR_ATTR(adc0calibVmin, 0644, ++ sen_get_cal, sen_set_cal, ADCCALVMIN | 0), ++ SENSOR_ATTR(adc1calibVmin, 0644, ++ sen_get_cal, sen_set_cal, ADCCALVMIN | 1), ++ SENSOR_ATTR(adc0calibCmax, 0644, ++ sen_get_cal, sen_set_cal, ADCCALCMAX | 0), ++ SENSOR_ATTR(adc1calibCmax, 0644, ++ sen_get_cal, sen_set_cal, ADCCALCMAX | 1), ++ SENSOR_ATTR(adc0calibCmin, 0644, ++ sen_get_cal, sen_set_cal, ADCCALCMIN | 0), ++ SENSOR_ATTR(adc1calibCmin, 0644, ++ sen_get_cal, sen_set_cal, ADCCALCMIN | 1), ++#endif ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ SENSOR_ATTR(firmware, 0640, bl_get_state, bl_start_load, 0), ++#endif ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ SENSOR_ATTR(counter0, 0666, cnt_get, cnt_set, 0), ++ SENSOR_ATTR(counter1, 0666, cnt_get, cnt_set, 1), ++ SENSOR_ATTR(counter2, 0666, cnt_get, cnt_set, 2), ++ SENSOR_ATTR(counter3, 0666, cnt_get, cnt_set, 3), ++ SENSOR_ATTR(counter4, 0666, cnt_get, cnt_set, 4), ++#endif ++}; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_ATOI ++static int sp_atoi(const uint8_t *buf, size_t len) { ++ int value = 0; ++ int i; ++ for(i=0; i<len; i++) { ++ if(buf[i] < '0') break; ++ if(buf[i] > '9') break; ++ value = value * 10 + buf[i] - '0'; ++ } ++ return value; ++} ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_UART ++static struct uart_driver uartextender_uart = { ++ .owner = THIS_MODULE, ++ .driver_name = "spi_uartextender", ++ .dev_name = UART_NAME, ++ .major = UART_MAJOR, ++ .minor = UART_MINOR, ++ .nr = UART_NR, ++ .cons = NULL, ++}; ++ ++static void uartextender_send_pck(struct supervisor_data *data, ++ uint8_t buf[XM_SPI_BUF_SIZE]) { ++ unsigned long flags; ++ spin_lock_irqsave(&data->lock, flags); ++ if(timer_pending(&data->ussendtimer)) del_timer(&data->ussendtimer); ++ if(data->ports_enabled) ++ mod_timer(&data->testtimer, jiffies + msecs_to_jiffies(100)); ++ spi_comm_msg(&data->spic, buf); ++ spin_unlock_irqrestore(&data->lock, flags); ++ ++} ++ ++static unsigned int uartextender_tx_empty(struct uart_port *port) { ++ return TIOCSER_TEMT; ++} ++ ++static void uartextender_set_mctrl(struct uart_port *port, unsigned int mctrl) { ++ return; ++} ++ ++static unsigned int uartextender_get_mctrl(struct uart_port *port) { ++ return (TIOCM_CTS|TIOCM_DSR); ++} ++ ++static void uartextender_start_tx(struct uart_port *uport) { ++ struct uartextender_port *port = (struct uartextender_port *) uport; ++ if(port->enabled == 0) ++ return; ++ port->tx = 1; ++} ++ ++static void uartextender_stop_tx(struct uart_port *uport) { ++ struct uartextender_port *port = (struct uartextender_port *) uport; ++ if(port->enabled == 0) ++ return; ++ port->tx = 0; ++} ++ ++static void uartextender_stop_rx(struct uart_port *uport) { ++ struct uartextender_port *port = (struct uartextender_port *) uport; ++ if(port->enabled == 0) ++ return; ++ port->rx = 1; ++} ++ ++static int uartextender_startup(struct uart_port *uport) { ++ struct uartextender_port *port = (struct uartextender_port *) uport; ++ pr_debug("startup port %i\n", uport->line); ++ if(port->enabled) return 0; ++ port->enabled = 1; ++ port->data->ports_enabled++; ++ return 0; ++} ++ ++static void uartextender_shutdown(struct uart_port *uport) { ++ struct uartextender_port *port = (struct uartextender_port *) uport; ++ pr_debug("shutdown port %i\n", uport->line); ++ if(!port->enabled) return; ++ port->enabled = 0; ++ port->data->ports_enabled--; ++} ++ ++static void uartextender_flush_buffer(struct uart_port *port) { ++ return; ++} ++ ++static void uartextender_set_termios(struct uart_port *port, ++ struct ktermios *termios, struct ktermios *old) { ++ struct uartextender_port *uport = (struct uartextender_port *) port; ++ uint8_t buf[XM_SPI_BUF_SIZE] = {0x80, port->line, 1, }; ++ unsigned int baud; ++ baud = uart_get_baud_rate(port, termios, old, 0, 32000000UL); ++ buf[3] = baud >> 16; ++ buf[4] = baud >> 8; ++ buf[5] = baud & 0xFF; ++ switch(termios->c_cflag & CSIZE) { ++ case CS5: buf[10] = 5; break; ++ case CS6: buf[10] = 6; break; ++ case CS7: buf[10] = 7; break; ++ default: buf[10] = 8; break; ++ } ++ if(termios->c_cflag & PARENB) { ++ if(termios->c_cflag & PARODD) ++ buf[11] = 2; ++ else ++ buf[11] = 1; ++ } ++ if(termios->c_cflag & CSTOPB) ++ buf[11] |= (1<<7); ++ uartextender_send_pck(uport->data, buf); ++}; ++ ++static const char *uartextender_type(struct uart_port *port) { ++ if(port->type == PORT_UARTEXTENDER) return "UARTEXTENDER"; ++ return NULL; ++} ++ ++static void uartextender_config_port(struct uart_port *port, int flags) { ++ if(flags & UART_CONFIG_TYPE) { ++ port->type = PORT_UARTEXTENDER; ++ } ++} ++ ++static int uartextender_verify_port(struct uart_port *port, ++ struct serial_struct *ser) { ++ if(port->type == PORT_UARTEXTENDER) return 0; ++ if(port->type == PORT_UNKNOWN) return 0; ++ return -EINVAL; ++} ++ ++static struct uart_ops uartextender_uops = { ++ .tx_empty = uartextender_tx_empty, ++ .set_mctrl = uartextender_set_mctrl, ++ .get_mctrl = uartextender_get_mctrl, ++ .stop_tx = uartextender_stop_tx, ++ .start_tx = uartextender_start_tx, ++ .stop_rx = uartextender_stop_rx, ++ .startup = uartextender_startup, ++ .shutdown = uartextender_shutdown, ++ .flush_buffer = uartextender_flush_buffer, ++ .set_termios = uartextender_set_termios, ++ .type = uartextender_type, ++ .config_port = uartextender_config_port, ++ .verify_port = uartextender_verify_port, ++}; ++ ++/********************************************************* ++ * UART Stuff ++ *********************************************************/ ++static int uartextender_send(struct uartextender_port *port) { ++ uint8_t buf[XM_SPI_BUF_SIZE] = {GE_USART(port->uart.line),}; ++ struct circ_buf *xmit = &port->uart.state->xmit; ++ int ret = 0; ++ if(!port->enabled) return 0; ++ if(port->uart.x_char) { ++ buf[1]++; ++ buf[buf[1]+1] = port->uart.x_char; ++ port->uart.x_char = 0; ++ } ++ if(uart_tx_stopped(&port->uart)) { ++ if(buf[1]) ++ uartextender_send_pck(port->data, buf); ++ return buf[1]; ++ } ++ while(!uart_circ_empty(xmit) && (buf[1] < (XM_SPI_BUF_SIZE-2))) { ++ buf[1]++; ++ buf[buf[1]+1] = xmit->buf[xmit->tail]; ++ xmit->tail = (xmit->tail+1)&(UART_XMIT_SIZE-1); ++ ret++; ++ } ++ if(buf[1]) ++ uartextender_send_pck(port->data, buf); ++ ++ if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&port->uart); ++ ++ return buf[1]; ++} ++ ++static void ue_noptrans(unsigned long _data) { ++ struct supervisor_data *data = (void *) _data; ++ char rbuf[XM_SPI_BUF_SIZE] = {}; ++ int i; ++ unsigned long flags; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return; ++#endif ++ spin_lock_irqsave(&data->lock, flags); ++ for(i=0;i<UART_NR;i++) { ++ if(uartextender_send(&data->port[i]) != 0) break; ++ } ++ if(i==UART_NR) ++ spi_comm_msg(&data->spic, rbuf); ++ spin_unlock_irqrestore(&data->lock, flags); ++} ++ ++static void ue_starttrans(unsigned long _data) { ++ struct supervisor_data *data = (void *) _data; ++ int i; ++ unsigned long flags; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return; ++#endif ++ spin_lock_irqsave(&data->lock, flags); ++ for(i=0;i<UART_NR;i++) { ++ if(uartextender_send(&data->port[i]) != 0) break; ++ } ++ if(i==UART_NR && data->ports_enabled) ++ mod_timer(&data->testtimer, jiffies + msecs_to_jiffies(100)); ++ spin_unlock_irqrestore(&data->lock, flags); ++} ++ ++static struct uart_port uartextender_porttemplate = { ++ .iotype = UPIO_MEM, ++ .ops = &uartextender_uops, ++ .type = PORT_UARTEXTENDER, ++ .fifosize = 10, ++}; ++ ++#endif // CONFIG_SUPERVISOR_UART ++ ++static void supervisor_rxinval(unsigned long _data) { ++ struct supervisor_data *data = (struct supervisor_data *) _data; ++ unsigned long flags; ++ spin_lock_irqsave(&data->lock, flags); ++ data->rx_valid = 0; ++ spin_unlock_irqrestore(&data->lock, flags); ++} ++ ++static void supervisor_send(unsigned long _data) { ++ struct supervisor_data *data = (struct supervisor_data *) _data; ++ unsigned long flags; ++ pr_debug("Send Msg\n"); ++ spin_lock_irqsave(&data->lock, flags); ++ if(data->tx[0][0]) { ++ data->last_valid = 1; ++ spi_comm_msg(&data->spic, (char *) data->tx); ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ } else if(data->cnt_tx_buf[0] && !data->cnt_tx_sent) { ++ data->last_valid = 1; ++ data->cnt_tx_sent = 1; ++ spi_comm_msg(&data->spic, data->cnt_tx_buf); ++#endif ++ } else { ++ data->last_valid = 0; ++ spi_comm_msg(&data->spic, (char *) data->tx); ++ } ++ memset(data->tx, 0, sizeof(data->tx)); ++ spin_unlock_irqrestore(&data->lock, flags); ++} ++ ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ ++static void supervisor_reset_send(struct supervisor_data *data, ++ char buf[XM_SPI_BUF_SIZE]) { ++ unsigned long flags; ++ spin_lock_irqsave(&data->lock, flags); ++ memcpy(data->cnt_tx_buf, buf, XM_SPI_BUF_SIZE); ++ data->cnt_tx_sent = 0; ++ if(!timer_pending(&data->sendtimer)) { ++ data->sendtimer.expires = ++ jiffies + msecs_to_jiffies(SUPERVISOR_SENDTO); ++ add_timer(&data->sendtimer); ++ } ++ spin_unlock_irqrestore(&data->lock, flags); ++} ++static void supervisor_cntreg_set(struct supervisor_data *data, int counter, ++ uint32_t value) { ++ char buf[XM_SPI_BUF_SIZE] = { GE_CNTER_SET, counter, ++ //value >> 24, value >> 16, value >> 8, value >> 0, ++ value >> 0, value >> 8, value >> 16, value >> 24, ++ }; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return; ++#endif ++ supervisor_reset_send(data, buf); ++} ++ ++static counter_t supervisor_cntreg_get(struct supervisor_data *data, int num) { ++ unsigned long flags; ++ char buf[XM_SPI_BUF_SIZE] = { GE_CNTER_GET, num, }; ++ counter_t ret; ++ int i; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return 0; ++#endif ++ pr_debug("Recv Counter\n"); ++ data->cnt_valid = 0; ++ do { ++ supervisor_reset_send(data, buf); ++ wait_event_interruptible_timeout(data->recvq, data->cnt_valid, ++ msecs_to_jiffies(10)); ++ if(data->cnt_valid) { ++ spin_lock_irqsave(&data->lock, flags); ++ ret = 0; ++ for(i=sizeof(ret);i>0;i--) { ++ ret = ret<<8; ++ ret += data->cnt_buf[i-1]; ++ } ++ spin_unlock_irqrestore(&data->lock, flags); ++ return ret; ++ } ++ } while(1); ++} ++ ++static ssize_t cnt_set(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ int value = sp_atoi(buf, len); ++ if((buf[0] == 'r') || (buf[0] == 'R')) ++ value = 0xFFFFFFFFUL; ++ supervisor_cntreg_set(data, attr->index, value); ++ return len; ++} ++ ++static ssize_t cnt_get(struct device *dev, ++ struct device_attribute *dattr, char *buf) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ return sprintf(buf, "%li\n", ++ (long) supervisor_cntreg_get(data, attr->index)); ++} ++#endif ++/********************************************************* ++ * Register handling ++ *********************************************************/ ++static void supervisor_setreg_mask(struct supervisor_data *data, int reg, ++ int port, int mask) { ++ int i; ++ unsigned long flags; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return; ++#endif ++ spin_lock_irqsave(&data->lock, flags); ++ for(i=0; i<ARRAY_SIZE(data->tx); i++) { ++ if((data->tx[i][0] == reg) && (data->tx[i][1] == port)) { ++ data->tx[i][2] |= mask; ++ spin_unlock_irqrestore(&data->lock, flags); ++ return; ++ } ++ if(data->tx[i][0] != 0) continue; ++ data->tx[i][0] = reg; ++ data->tx[i][1] = port; ++ data->tx[i][2] = mask; ++ if(!timer_pending(&data->sendtimer)) { ++ data->sendtimer.expires = ++ jiffies + msecs_to_jiffies(SUPERVISOR_SENDTO); ++ add_timer(&data->sendtimer); ++ } ++ spin_unlock_irqrestore(&data->lock, flags); ++ return; ++ } ++ // Queue is full, schedule transfer and open next. ++ del_timer(&data->sendtimer); ++ spin_unlock_irqrestore(&data->lock, flags); ++ supervisor_send((unsigned long) data); ++ data->tx[0][0] = reg; ++ data->tx[0][1] = port; ++ data->tx[0][2] = mask; ++ for(i=1;i<ARRAY_SIZE(data->tx); i++) { ++ data->tx[i][0] = 0; ++ data->tx[i][1] = 0; ++ data->tx[i][2] = 0; ++ } ++ data->sendtimer.expires = jiffies + msecs_to_jiffies(SUPERVISOR_SENDTO); ++ add_timer(&data->sendtimer); ++} ++ ++static void supervisor_setreg(struct supervisor_data *data, int reg, int pin) { ++ supervisor_setreg_mask(data, reg, pin>>3, 1<<(pin &0x07)); ++} ++ ++static int supervisor_getreg_raw(struct supervisor_data *data, int reg, ++ int port, int testport) { ++ int i, ignore = 0; ++ unsigned long flags; ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) return 0; ++#endif ++ do { ++ spin_lock_irqsave(&data->lock, flags); ++ for(i=0;i<ARRAY_SIZE(data->rx);i++) { ++ if(!data->rx_valid) break; ++ if(data->rx[i][0] == 0) break; ++ if(data->rx[i][0] != reg) continue; ++ if(testport && (data->rx[i][1] != port)) continue; ++ spin_unlock_irqrestore(&data->lock, flags); ++ return (data->rx[i][1]<<8)|data->rx[i][2]; ++ } ++ // No actual data got ++ if(!ignore) { ++ supervisor_setreg_mask(data, reg, port, 0); ++ del_timer(&data->recvtimer); ++ } ++ ignore = !ignore; ++ data->rx_valid = 0; ++ spin_unlock_irqrestore(&data->lock, flags); ++ wait_event_interruptible(data->recvq, data->rx_valid); ++ } while(1); ++} ++ ++static int supervisor_getreg_mask(struct supervisor_data *data, int reg, ++ int port, int mask) { ++ return supervisor_getreg_raw(data, reg, port, 1) & mask; ++} ++ ++static int supervisor_getreg(struct supervisor_data *data, int reg, int pin) { ++ return supervisor_getreg_mask(data, reg, pin>>3, 1<<(pin&0x07))?1:0; ++} ++ ++/********************************************************* ++ * GPIO handling ++ *********************************************************/ ++ ++static int supervisor_dirin(struct gpio_chip *gpio, unsigned int offset) { ++ struct supervisor_data *data = ++ container_of(gpio, struct supervisor_data, gpioc); ++ int ret = 0; ++ if(offset < 8) { ++ supervisor_setreg(data, GE_IER, offset); ++ } else if(offset < 24) { ++ ret = -EACCES; ++ } ++ return ret; ++} ++ ++static int supervisor_dirout(struct gpio_chip *gpio, unsigned int offset, ++ int value) { ++ struct supervisor_data *data = ++ container_of(gpio, struct supervisor_data, gpioc); ++ int ret = 0; ++ int reg = value?GE_OER:GE_ODR; ++ if(offset < 8) { ++ supervisor_setreg(data, GE_IDR, offset); ++ } ++ if(offset >= 24) { ++ ret = -EACCES; ++ } else if (ret == 0) { ++ supervisor_setreg(data, reg, offset); ++ } ++ return ret; ++} ++ ++static int supervisor_get(struct gpio_chip *gpio, unsigned int offset) { ++ struct supervisor_data *data = ++ container_of(gpio, struct supervisor_data, gpioc); ++ int ret = 0; ++ ret = supervisor_getreg(data, GE_PPR, offset)?1:0; ++ return ret; ++} ++ ++static void supervisor_set(struct gpio_chip *gpio, unsigned int offset, int value) { ++ struct supervisor_data *data = ++ container_of(gpio, struct supervisor_data, gpioc); ++ int reg = value?GE_OER:GE_ODR; ++ ++ supervisor_setreg(data, reg, offset); ++} ++ ++#ifdef CONFIG_SUPERVISOR_IRQ ++static int supervisor_to_irq(struct gpio_chip *gpio, unsigned offset) { ++ struct supervisor_data *data = ++ container_of(gpio, struct supervisor_data, gpioc); ++ pr_debug("gpio %i to irq %i\n", offset, data->firq+offset); ++ if(offset < 16) { ++ return data->firq + offset; ++ } ++ return -1; ++} ++#else ++#define supervisor_to_irq NULL ++#endif ++ ++static struct gpio_chip supervisor_gpio = { ++ .owner = THIS_MODULE, ++ .direction_input = supervisor_dirin, ++ .direction_output = supervisor_dirout, ++ .get = supervisor_get, ++ .set = supervisor_set, ++ .to_irq = supervisor_to_irq, ++ .base = -1, ++ .ngpio = 16, ++ .can_sleep = 1, ++ .label = "supervisor0", ++}; ++ ++#ifdef CONFIG_SUPERVISOR_IRQ ++/********************************************************* ++ * IRQ Stuff ++ *********************************************************/ ++ ++static void supervisor_irq_mask(unsigned irq) { ++ struct supervisor_data *data = get_irq_chip_data(irq); ++ ++ supervisor_setreg(data, GE_IDR, irq - data->firq); ++} ++ ++static void supervisor_irq_unmask(unsigned irq) { ++ struct supervisor_data *data = get_irq_chip_data(irq); ++ ++ supervisor_setreg(data, GE_IER, irq - data->firq); ++} ++ ++static int supervisor_irq_type(unsigned irq, unsigned type) { ++ if (type != IRQ_TYPE_EDGE_BOTH && type != IRQ_TYPE_NONE) ++ return -EINVAL; ++ return 0; ++} ++ ++static struct irq_chip supervisor_irq = { ++ .name = "supervisor", ++ .mask = supervisor_irq_mask, ++ .unmask = supervisor_irq_unmask, ++ .set_type = supervisor_irq_type, ++}; ++ ++#endif ++ ++#if defined(CONFIG_SUPERVISOR_UART) || defined(CONFIG_SUPERVISOR_IRQ) ++ ++static irqreturn_t supervisor_irqh(int irq, void *_data) { ++ struct supervisor_data *data = _data; ++#ifdef CONFIG_SUPERVISOR_UART ++ unsigned long flags; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_IRQ ++ supervisor_setreg(data, GE_ISR, 0); ++ supervisor_setreg(data, GE_ISR, 8); ++#endif ++#ifdef CONFIG_SUPERVISOR_UART ++ spin_lock_irqsave(&data->lock, flags); ++ if(timer_pending(&data->ussendtimer)) ++ mod_timer(&data->ussendtimer, jiffies + msecs_to_jiffies(1)); ++ spin_unlock_irqrestore(&data->lock, flags); ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++#endif ++ ++/********************************************************* ++ * Buzzer ++ *********************************************************/ ++#ifdef CONFIG_SUPERVISOR_BUZZER ++static ssize_t sp_buzzer(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ int value = sp_atoi(buf, len); ++ supervisor_setreg_mask(data, attr->index, value >> 8, value & 0xFFU); ++ return len; ++} ++#endif ++ ++/********************************************************* ++ * A/D Converter ++ *********************************************************/ ++#ifdef CONFIG_SUPERVISOR_ADC ++static ssize_t sen_val(struct device *dev, ++ struct device_attribute *dattr, char *buf) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ return sprintf(buf, "%i\n", ++ supervisor_getreg_raw(data, attr->index, 0, 0)); ++} ++ ++static ssize_t sen_get_type(struct device *dev, ++ struct device_attribute *dattr, char *buf) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ return sprintf(buf, "%s\n", ++ (data->sensortype[attr->index] == SUP_ADC_0_10_V)? ++ "[voltage] current":"voltage [current]"); ++} ++ ++#define _cal2num(attr) ((attr) & 0xFFU) ++static uint8_t _get_cal_byte(int attr) { ++ switch(attr & 0xFF00U) { ++ case ADCCALVMIN: return GE_ADCSET_V|GE_ADCSET_CALMIN; ++ case ADCCALVMAX: return GE_ADCSET_V|GE_ADCSET_CALMAX; ++ case ADCCALCMIN: return GE_ADCSET_I|GE_ADCSET_CALMIN; ++ case ADCCALCMAX: return GE_ADCSET_I|GE_ADCSET_CALMAX; ++ } ++ return 0; ++} ++ ++static ssize_t sen_set_type(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ int value = ((buf[0]|(1<<5)) == 'v')?GE_ADCSET_V:GE_ADCSET_I; ++ ++ data->sensortype[attr->index] = value?SUP_ADC_4_20_mA:SUP_ADC_0_10_V; ++ supervisor_setreg_mask(data, GE_ADCSET, _cal2num(attr->index), value); ++ return len; ++} ++ ++static ssize_t sen_get_cal(struct device *dev, ++ struct device_attribute *dattr, char *buf) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ ++ // invalidate cache, as the result may come from another sensor ++ data->rx_valid=0; ++ ++ supervisor_setreg_mask(data, GE_ADCSET, _cal2num(attr->index), ++ _get_cal_byte(attr->index)); ++ return sprintf(buf, "%i\n", supervisor_getreg_raw(data, ++ GE_ADCGCAL(_cal2num(attr->index)), 0, 0)); ++} ++ ++static ssize_t sen_set_cal(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(dattr); ++ int value = sp_atoi(buf, len); ++ ++ supervisor_setreg_mask(data, GE_ADCSET, _cal2num(attr->index), ++ _get_cal_byte(attr->index)); ++ supervisor_setreg_mask(data, GE_ADCSCAL(_cal2num(attr->index)), ++ value >> 8, value & 0xFFU); ++ return len; ++} ++ ++#endif ++ ++/********************************************************* ++ * Protocol Stuff ++ *********************************************************/ ++#ifndef CONFIG_SUPERVISOR_NOCHECK ++static int supervisor_init_cb(char buf[XM_SPI_BUF_SIZE], struct spi_comm *spi) { ++ struct supervisor_data *data = ++ container_of(spi, struct supervisor_data, spic); ++ if(buf == NULL) { ++ dev_info(&spi->spi->dev, "got init-pck NULL spic:%i, spi:%i\n", ++ spi->state, spi->msg_state); ++ if(spi->state == SPIC_CRCERR) return -1; ++ return 0; ++ } ++ dev_info(&spi->spi->dev, "got init-pck '%x%s'\n", buf[0], buf +1); ++ data->found = (strncmp(buf+1, SPI_VERSION_STRING, ++ strlen(SPI_VERSION_STRING)) == 0); ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if((!data->found) && strncmp(buf + 1, SPI_BL_VERSION_STRING, ++ strlen(SPI_BL_VERSION_STRING)) == 0) { ++ dev_warn(&spi->spi->dev, ++ "Supervisor is in Bootloadermode during init!\n" ++ "Supply a new firmware first!"); ++ data->found = 1; ++ data->bootloader = 1; ++ } ++#endif ++ wake_up(&data->recvq); ++ return 0; ++} ++#endif ++ ++static int supervisor_cb(char buf[XM_SPI_BUF_SIZE], struct spi_comm *spi) { ++ struct supervisor_data *data = ++ container_of(spi, struct supervisor_data, spic); ++ int i, valid; ++ unsigned long flags, expires; ++#ifdef CONFIG_SUPERVISOR_IRQ ++ int pin, offset; ++#endif ++#ifdef CONFIG_SUPERVISOR_UART ++ struct uart_port *port; ++#endif ++ ++ if(buf == NULL) { ++ if(spi->state == SPIC_CRCERR) return -1; // resend last pck ++ return 0; ++ } ++ pr_debug("valid data received\n"); ++ spin_lock_irqsave(&data->lock, flags); ++#ifdef CONFIG_SUPERVISOR_UART ++ if((GE_BUSART(buf[0]) >= 0) && (GE_BUSART(buf[0]) < UART_NR) && ++ (data->port[GE_BUSART(buf[0])].enabled)) { ++ port = &data->port[GE_BUSART(buf[0])].uart; ++ pr_debug("p%02x %p (%ib): ", buf[0], port, buf[1]); ++ for(i=0; i<buf[1]; i++) { ++#ifdef DEBUG ++ printk("%02x ", buf[i+2]); ++#endif ++ if(uart_handle_sysrq_char(port, buf[i+2])) continue; ++ uart_insert_char(port, 0, 0, buf[i+2], 0); ++ } ++#ifdef DEBUG ++ printk("\n"); ++#endif ++ tty_flip_buffer_push(port->state->port.tty); ++ } ++#endif ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ if(buf[0] == GE_CNTER_GET) { ++ memcpy(data->cnt_buf, buf + 2, sizeof(counter_t)); ++ data->cnt_valid = 1; ++ } else if(buf[0] != GE_CNTER_SET) { ++#endif ++ expires = jiffies + msecs_to_jiffies(100); ++ mod_timer(&data->recvtimer, expires); ++ memcpy(data->rx, buf, XM_SPI_BUF_SIZE); ++ data->rx_valid = 1; ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ } ++#endif ++ wake_up(&data->recvq); ++ /* If the device send valid data, it is possible, that it has more ++ * data left. So schedule a new transmission then */ ++ valid = 0; ++ for(i=0; i<ARRAY_SIZE(data->rx); i++) { ++ if(data->rx[i][0] == 0x00) continue; // No data ++ valid = 1; ++#ifdef CONFIG_SUPERVISOR_IRQ ++ if(data->rx[i][0] != GE_ISR) continue; ++ offset = 8 * data->rx[i][1] + data->firq; ++ for(pin=0;pin<8;pin++) { ++ if(!(data->rx[i][2] & (1<<pin))) ++ continue; ++ pr_debug("handle IRQ %i\n", offset + pin); ++ generic_handle_irq(offset + pin); ++ } ++#else ++ break; ++#endif ++ } ++#ifdef CONFIG_SUPERVISOR_UART ++ for(i=0; i<UART_NR; i++) { ++ if(uartextender_send(&data->port[i]) != 0) break; ++ } ++ if(i==UART_NR && ((buf[0] & 0x90) == 0x90)) // There might be more data ++ mod_timer(&data->ussendtimer, jiffies + msecs_to_jiffies(10)); ++ if(data->ports_enabled) ++ mod_timer(&data->testtimer, jiffies + msecs_to_jiffies(100)); ++#endif ++ if(valid || data->last_valid) { ++ mod_timer(&data->sendtimer, ++ jiffies + msecs_to_jiffies(SUPERVISOR_SENDTO)); ++ } ++ spin_unlock_irqrestore(&data->lock, flags); ++ return 0; ++} ++ ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++/********************************************************* ++ * Bootloader logic ++ *********************************************************/ ++static int supervisor_bootloader_cb(char buf[XM_SPI_BUF_SIZE], ++ struct spi_comm *spi) { ++ unsigned long flags; ++ struct supervisor_data *data = ++ container_of(spi, struct supervisor_data, spic); ++ ++ if(buf == NULL) { ++ if(spi->state == SPIC_CRCERR) return -1; // resend last pck ++ return 0; ++ } ++ pr_debug("valid data received\n"); ++ spin_lock_irqsave(&data->lock, flags); ++ memcpy(data->blbuf, buf, XM_SPI_BUF_SIZE); ++ wake_up(&data->blw); ++ spin_unlock_irqrestore(&data->lock, flags); ++ return 0; ++} ++ ++#define bl_mk_cmd(buf, cmd, size) _bl_mk_cmd(buf, GE_BL_CMD_ ## cmd, size) ++static void _bl_mk_cmd(char buf[XM_SPI_BUF_SIZE], char cmd, int size) { ++ buf[0] = GE_BL_CMD; ++ buf[1] = cmd; ++ buf[2] = size >> 8; ++ buf[3] = size & 0xFFU; ++} ++ ++static void bl_mk_data(char buf[XM_SPI_BUF_SIZE], int addr, const char *data, ++ int size) { ++ int i; ++ buf[0] = GE_BL_DATA; ++ buf[1] = addr >> 8; ++ buf[2] = addr & 0xFFU; ++ //buf[3] = (1<<size)-1; ++ for(i=0; i<size; i++) buf[i+4] = data[i]; ++} ++ ++static void bl_mk_chk(char buf[XM_SPI_BUF_SIZE], int start, int end, long seed) ++{ ++ buf[0] = GE_BL_CHKSUM; ++ buf[1] = start >> 16; ++ buf[2] = start >> 8; ++ buf[3] = start; ++ buf[4] = 0; ++ buf[5] = end >> 16; ++ buf[6] = end >> 8; ++ buf[7] = end; ++ buf[8] = seed >> 24; ++ buf[9] = seed >> 16; ++ buf[10] = seed >> 8; ++ buf[11] = seed; ++} ++ ++static void update_thread(struct work_struct *work) { ++ struct supervisor_data *data = ++ container_of(work, struct supervisor_data, blwork); ++ const struct firmware *fw; ++ int addr, i, counter; ++ int blpagecount, blpagesize; ++ int status, size, chsize; ++ unsigned long flags; ++ char buf[XM_SPI_BUF_SIZE] = {}; ++ char *_dbuf; ++#define dbuf(i) (&_dbuf[(i) * XM_SPI_BUF_SIZE]) ++ ++ init_waitqueue_head(&data->blw); ++ data->blstate = BL_WAIT_FW; ++ status = request_firmware(&fw, "adb4000.bin", &data->spic.spi->dev); ++ if(status != 0) { ++ dev_warn(&data->spic.spi->dev, "Firmware not found"); ++ data->blstate = BL_ERR; ++ goto bl_up_fwnotfound; ++ } ++ ++ dev_info(&data->spic.spi->dev, "Contacting Bootloader..."); ++ data->blstate = BL_CONTACTING; ++ data->bootloader = 1; ++ data->spic.cb = supervisor_bootloader_cb; ++ do { ++ spin_lock_irqsave(&data->lock, flags); ++ buf[0] = GE_BL_INFO; ++ msleep(40); ++ spi_comm_msg(&data->spic, buf); ++ wait_event_interruptible(data->blw, 1); ++ spin_unlock_irqrestore(&data->lock, flags); ++ blpagesize = data->blbuf[2]<<8|data->blbuf[3]; ++ blpagecount = data->blbuf[4]<<8|data->blbuf[5]; ++ } while((data->blbuf[0] != GE_BL_INFO) || (blpagesize == 0) || (blpagecount == 0) ); ++ dev_info(&data->spic.spi->dev, ++ "Bootloader has %iB per %i pages. Size is %iB", ++ blpagesize, blpagecount, blpagesize * blpagecount); ++ if(fw->size > (blpagesize * blpagecount)) { ++ dev_warn(&data->spic.spi->dev, "Firmware too large"); ++ data->blstate = BL_ERR; ++ goto bl_up_tolarge; ++ } ++ ++ dev_info(&data->spic.spi->dev, "Loading Firmware..."); ++ data->blstate = BL_LOADING; ++ _dbuf = kzalloc(((blpagesize / 8) + 4) * XM_SPI_BUF_SIZE, GFP_KERNEL); ++ for(addr = 0; addr < fw->size; addr += blpagesize) { ++ //spin_lock_irqsave(&data->lock, flags); ++ bl_mk_cmd(dbuf(0), LOAD, addr / blpagesize); ++ spi_comm_msg(&data->spic, dbuf(0)); ++ size = blpagesize; ++ if((fw->size - addr) < size) size = fw->size - addr; ++ for(i = addr % blpagesize; i < size; i+=chsize) { ++ chsize = fw->size - addr -i; ++ if(chsize > size - i) chsize = size - i; ++ if(chsize > 8) chsize = 8; ++ //if(i%0x80 == 0) msleep(100); ++ bl_mk_data(dbuf(i/8+3), i, fw->data + addr + i, chsize); ++ spi_comm_msg(&data->spic, dbuf(i/8+1)); ++ } ++ //spi_comm_msg(&data->spic, dbuf(1)); ++ bl_mk_cmd(dbuf(2), WRITE, addr/blpagesize); ++ //spi_comm_msg(&data->spic, dbuf(2)); ++ //spin_unlock_irqrestore(&data->lock, flags); ++ // Wait for write done ++ counter = 0; ++ while((data->blbuf[0] != GE_BL_CMD) || ++ (data->blbuf[1] != GE_BL_CMD_WRITE)) { ++ wait_event_interruptible_timeout(data->blw, 1, ++ msecs_to_jiffies(100)); ++ if(counter == 0) { ++ spi_comm_msg(&data->spic, dbuf(2)); ++ counter = 10; ++ } else { ++ spi_comm_msg(&data->spic, dbuf(1)); ++ counter --; ++ } ++ msleep(40); ++ } ++ } ++ ++ dev_info(&data->spic.spi->dev, "Verifying Firmware..."); ++ data->blstate = BL_VERIFYING; ++ spi_comm_msg(&data->spic, dbuf(1)); ++ bl_mk_chk(buf, 0, fw->size, CRC32_SEED); ++ counter = 0; ++ do { ++ wait_event_interruptible_timeout(data->blw, 1, ++ msecs_to_jiffies(100)); ++ if(counter == 0) { ++ spi_comm_msg(&data->spic, buf); ++ counter = 10; ++ } else { ++ spi_comm_msg(&data->spic, dbuf(1)); ++ counter --; ++ } ++ msleep(40); ++ } while(data->blbuf[0] != GE_BL_CHKSUM); ++ ++ i = data->blbuf[8]<<24; ++ i |= data->blbuf[9]<<16; ++ i |= data->blbuf[10]<<8; ++ i |= data->blbuf[11]; ++ if(i != crc32(CRC32_SEED, fw->data, fw->size)) { ++ data->blstate = BL_ERR; ++ dev_warn(&data->spic.spi->dev, "Checksum doesn't match"); ++ goto bl_up_chkerr; ++ } ++ ++ dev_info(&data->spic.spi->dev, "Uploading Firmware done"); ++ data->blstate = BL_DONE; ++ bl_mk_cmd(buf, RESET, 0); ++ spi_comm_msg(&data->spic, buf); ++ ++bl_up_chkerr: ++ kfree(_dbuf); ++bl_up_tolarge: ++ data->spic.cb = supervisor_cb; ++ data->bootloader = 0; ++ release_firmware(fw); ++bl_up_fwnotfound: ++// destroy_workqueue(data->blwq); ++ return; ++} ++ ++ ++static void bl_init(struct supervisor_data *data) { ++ data->blwq = create_singlethread_workqueue("Supervisor_update"); ++ INIT_WORK(&data->blwork, update_thread); ++ queue_work(data->blwq, &data->blwork); ++} ++ ++static ssize_t bl_start_load(struct device *dev, ++ struct device_attribute *dattr, const char *buf, size_t len) { ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ bl_init(data); ++ return len; ++} ++ ++static ssize_t bl_get_state(struct device *dev, ++ struct device_attribute *dattr, char *buf) { ++ const char *msg = NULL; ++ struct supervisor_data *data = dev_get_drvdata(dev); ++ switch(data->blstate) { ++ case BL_INIT: msg = "not used"; break; ++ case BL_WAIT_FW: msg = "waiting for firmware"; break; ++ case BL_CONTACTING: msg = "contacting"; break; ++ case BL_LOADING: msg = "uploading"; break; ++ case BL_VERIFYING: msg = "verifying"; break; ++ case BL_DONE: msg = "OK"; break; ++ case BL_ERR: msg = "Error"; break; ++ } ++ return sprintf(buf, "%s\n", msg); ++} ++#endif ++ ++ ++static int __devinit supervisor_probe(struct spi_device *spi) { ++ struct supervisor_data *data; ++ int status = 0; ++ int i; ++ ++ i = 0; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if(!data) ++ return -ENOMEM; ++ status = spi_comm_init(&data->spic, spi, 10, (3*32+9)); ++ if(status < 0) goto spi_comm_err; ++ ++ data->rx_valid = 0; ++ init_waitqueue_head(&data->recvq); ++ spin_lock_init(&data->lock); ++ ++ // Timer ++ setup_timer(&data->recvtimer, supervisor_rxinval, (unsigned long) data); ++ setup_timer(&data->sendtimer, supervisor_send, (unsigned long) data); ++ ++ // SPI ++ spi_set_drvdata(spi, data); ++#ifndef CONFIG_SUPERVISOR_NOCHECK ++ data->spic.cb = supervisor_init_cb; ++ for(i=3;i>0;i--) { ++ unsigned long flags; ++ char buf[XM_SPI_BUF_SIZE] = { GE_VERSION, }; ++ spin_lock_irqsave(&data->lock, flags); ++ if(spi_comm_busy(&data->spic)) { ++ msleep(100); ++ continue; ++ } ++ spi_comm_msg(&data->spic, buf); ++ spin_unlock_irqrestore(&data->lock, flags); ++ wait_event_interruptible_timeout(data->recvq, data->found, ++ msecs_to_jiffies(20)); ++ if(data->found) break; ++ } ++ if(!data->found) { ++ dev_warn(&spi->dev, "No Supervisor-Chip found!\n"); ++ status = -ENODEV; ++ goto notfound; ++ } ++#endif ++ data->spic.cb = supervisor_cb; ++#ifdef CONFIG_SUPERVISOR_BOOTLOADER ++ if(data->bootloader) bl_init(data); ++#endif ++ ++ // GPIO ++ memcpy(&data->gpioc, &supervisor_gpio, sizeof(supervisor_gpio)); ++ data->gpioc.dev = &spi->dev; ++ status = gpiochip_add(&data->gpioc); ++ if(status < 0) ++ goto gpioerr; ++ ++#ifdef CONFIG_SUPERVISOR_LED ++ // LEDs ++ for(i=0;i<ARRAY_SIZE(data->leds);i++) { ++ data->leds[i].name = kasprintf(GFP_KERNEL, "%s:led%i", ++ dev_name(&spi->dev), i); ++ if(data->leds[i].name == NULL) { ++ status = -ENOMEM; ++ goto ledmemerr; ++ } ++ data->leds[i].gpio = data->gpioc.base + i + 3; ++ data->leds[i].active_low = 1; ++ } ++ data->leds[0].default_trigger = "heartbeat"; ++ data->leds[1].default_trigger = "nand-disk"; ++ data->leds[2].default_trigger = "mmc0"; ++ data->leds[3].default_trigger = "mmc1"; ++ data->leds_plat.num_leds = i; ++ data->leds_plat.leds = data->leds; ++ data->leds_dev.name = "leds-gpio"; ++ data->leds_dev.dev.platform_data = &data->leds_plat; ++ status = platform_device_register(&data->leds_dev); ++ if(status < 0) ++ goto lederr; ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_IRQ ++ data->firq = data->gpioc.base; ++ for(i=data->firq;i<(data->firq + 16);i++) { ++ set_irq_chip(i, &supervisor_irq); ++ set_irq_handler(i, handle_simple_irq); ++ set_irq_flags(i, IRQF_VALID); ++ set_irq_chip_data(i, data); ++ } ++#endif ++ ++#ifdef CONFIG_SUPERVISOR_UART ++ setup_timer(&data->ussendtimer, ue_noptrans, (unsigned long) data); ++ setup_timer(&data->testtimer, ue_starttrans, (unsigned long) data); ++ ++ for(i=0; i < UART_NR; i++) { ++ memcpy(&data->port[i].uart, &uartextender_porttemplate, ++ sizeof(uartextender_porttemplate)); ++ pr_debug("set up uart %i (%p)\n", i, &data->port[i].uart); ++ data->port[i].uart.line = i; ++ data->port[i].uart.dev = &spi->dev; ++ data->port[i].data = data; ++ status = uart_add_one_port(&uartextender_uart, ++ &data->port[i].uart); ++ if(status) ++ pr_warning("Can't add port%i (%i)\n", i, status); ++ } ++#endif ++ ++#if defined(CONFIG_SUPERVISOR_IRQ) || defined(CONFIG_SUPERVISOR_UART) ++// data->hirq = (int) spi_get_drvdata(spi); ++ data->hirq = gpio_to_irq(4*32+18); ++ if(data->hirq <= 0) { ++ dev_warn(&spi->dev, "No interrupt!\n"); ++ goto irqerr; ++ } ++ status = request_irq(data->hirq, supervisor_irqh, IRQ_TYPE_EDGE_BOTH, ++ "supervisor", data); ++ if(status < 0) ++ goto irqerr; ++#endif ++#ifdef CONFIG_SUPERVISOR_SYSFS ++ for(i=0; i< ARRAY_SIZE(sp_sensors); i++) { ++ if((status = device_create_file(&spi->dev, ++ &sp_sensors[i].dev_attr))<0) ++ goto sysfserr; ++ } ++#endif ++#ifdef CONFIG_SUPERVISOR_CNTIN ++ data->cnt_valid = 0; ++#endif ++ ++ return 0; ++ ++#ifdef CONFIG_SUPERVISOR_SYSFS ++ i = ARRAY_SIZE(sp_sensors); ++sysfserr: ++ for(;i>0;i--) ++ device_remove_file(&spi->dev, &sp_sensors[i-1].dev_attr); ++#endif ++#if defined(CONFIG_SUPERVISOR_IRQ) || defined(CONFIG_SUPERVISOR_UART) ++ free_irq(data->hirq, data); ++irqerr: ++#endif ++#ifdef CONFIG_SUPERVISOR_LED ++ platform_device_unregister(&data->leds_dev); ++ i = ARRAY_SIZE(data->leds); ++ledmemerr: ++ for(;i>0;i--) ++ kfree(data->leds[i].name); ++lederr: ++#endif ++ if(gpiochip_remove(&data->gpioc) < 0) ++ dev_warn(&spi->dev, "Can't remove GPIO-CHIP!"); ++gpioerr: ++notfound: ++ spi_comm_destroy(&data->spic); ++spi_comm_err: ++ kfree(data); ++ return status; ++} ++ ++static int __devexit supervisor_remove(struct spi_device *spi) { ++ struct supervisor_data *data = spi_get_drvdata(spi); ++ int i; ++ ++#ifdef CONFIG_SUPERVISOR_SYSFS ++ for(i=0; i<ARRAY_SIZE(sp_sensors); i++) ++ device_remove_file(&spi->dev, &sp_sensors[i].dev_attr); ++#endif ++#if defined(CONFIG_SUPERVISOR_IRQ) || defined(CONFIG_SUPERVISOR_UART) ++ free_irq(data->hirq, data); ++#endif ++#ifdef CONFIG_SUPERVISOR_LED ++ platform_device_unregister(&data->leds_dev); ++ for(i=0;i<ARRAY_SIZE(data->leds);i++) ++ kfree(data->leds[i].name); ++#else ++ i = 0; ++#endif ++ if(gpiochip_remove(&data->gpioc) < 0) ++ dev_warn(&spi->dev, "Can't remove GPIO-CHIP!"); ++ spi_comm_destroy(&data->spic); ++ kfree(data); ++ return 0; ++} ++ ++static struct spi_driver supervisor_driver = { ++ .driver = { ++ .name = "supervisor", ++ .owner = THIS_MODULE, ++ }, ++ .probe = supervisor_probe, ++ .remove = __devexit_p(supervisor_remove), ++}; ++ ++/********************************************************* ++ * Module Stuff ++ *********************************************************/ ++static int __init supervisor_init(void) { ++ int status; ++#ifdef CONFIG_SUPERVISOR_UART ++ status = uart_register_driver(&uartextender_uart); ++ if(status < 0) ++ goto uart_err; ++#endif ++ status = spi_register_driver(&supervisor_driver); ++ if(status < 0) ++ goto spi_err; ++ ++ return 0; ++ ++ spi_unregister_driver(&supervisor_driver); ++spi_err: ++#ifdef CONFIG_SUPERVISOR_UART ++ uart_unregister_driver(&uartextender_uart); ++uart_err: ++#endif ++ return status; ++} ++module_init(supervisor_init); ++ ++static void __exit supervisor_exit(void) { ++ spi_unregister_driver(&supervisor_driver); ++#ifdef CONFIG_SUPERVISOR_UART ++ uart_unregister_driver(&uartextender_uart); ++#endif ++} ++module_exit(supervisor_exit); ++ ++MODULE_AUTHOR("Benjamin Tietz <benjamin.tietz@in-circuit.de>"); ++MODULE_DESCRIPTION("ADB4000 Supervisor"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/misc/adb4000/supervisor.h b/drivers/misc/adb4000/supervisor.h +new file mode 100644 +index 0000000..6fdff83 +--- /dev/null ++++ b/drivers/misc/adb4000/supervisor.h +@@ -0,0 +1,46 @@ ++#ifndef __GPIOEXT_H ++#define __GPIOEXT_H ++ ++#define GE_SOR 0x12 ++#define GE_SIR 0x11 ++#define GE_IOSR 0x10 ++#define GE_PPR 0x20 ++#define GE_ISR 0x33 ++#define GE_IER 0x32 ++#define GE_IDR 0x31 ++#define GE_IMR 0x30 ++#define GE_PER 0x42 ++#define GE_PDR 0x41 ++#define GE_PSR 0x40 ++#define GE_OER 0x52 ++#define GE_ODR 0x51 ++#define GE_OSR 0x50 ++#define GE_BUZZER 0x60 ++#define GE_ADCSET 0x6F ++#define GE_ADCSET_V (0<<0) ++#define GE_ADCSET_I (1<<0) ++#define GE_ADCSET_NOCAL (0<<1) ++#define GE_ADCSET_CALMIN (1<<1) ++#define GE_ADCSET_CALMAX (2<<1) ++#define GE_ADCSET_CALMASK (3<<1) ++#define GE_ADC(x) (0x70 + (x)) ++#define GE_ADCCALIB(x) (0x74 + (x)) ++#define GE_ADCGCAL(x) (0x78 + (x)) ++#define GE_ADCSCAL(x) (0x7C + (x)) ++#define GE_SETUP_USART 0x80 ++#define GE_VERSION 0x81 ++#define GE_USART(x) (0x90 + (x)) ++#define GE_BUSART(x) ((int) (x) - 0x90) ++#define GE_CNTER_GET 0xA0 ++#define GE_CNTER_SET 0xA1 ++#define GE_BL_INFO 0xF0 ++#define GE_BL_CMD 0xF1 ++#define GE_BL_CMD_ERASEALL 0x10 ++#define GE_BL_CMD_ERASE 0x11 ++#define GE_BL_CMD_WRITE 0x12 ++#define GE_BL_CMD_LOAD 0x13 ++#define GE_BL_CMD_RESET 0xFF ++#define GE_BL_DATA 0xF2 ++#define GE_BL_CHKSUM 0xF3 ++ ++#endif +-- +1.7.3.3 + |