From: Viresh Kumar Date: Wed, 21 Jan 2015 10:40:40 +0000 (+0530) Subject: greybus: Remove "-gb" suffix from .c files X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=5357cf323110ee4a3f4a12870618eca28672c7b9;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git greybus: Remove "-gb" suffix from .c files Some files are prefixed with "gb-" and some are suffixed with "-gb". The rationale behind the first one is that the modules would be named so, i.e. gb-*.ko. But there is no reason to keep the "-gb" suffix in the second case. Remove the unnecessary suffix. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a5d5470974ba..55b4a37c58fb 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,12 +10,12 @@ greybus-y := core.o \ operation.o gb-phy-y := gpb.o \ - sdio-gb.o \ - uart-gb.o \ - pwm-gb.o \ - gpio-gb.o \ - i2c-gb.o \ - usb-gb.o + sdio.o \ + uart.o \ + pwm.o \ + gpio.o \ + i2c.o \ + usb.o obj-m += greybus.o obj-m += gb-phy.o diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c deleted file mode 100644 index 4997588e2617..000000000000 --- a/drivers/staging/greybus/gpio-gb.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * GPIO Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_gpio_line { - /* The following has to be an array of line_max entries */ - /* --> make them just a flags field */ - u8 active: 1, - direction: 1, /* 0 = output, 1 = input */ - value: 1; /* 0 = low, 1 = high */ - u16 debounce_usec; -}; - -struct gb_gpio_controller { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - u8 line_max; /* max line number */ - struct gb_gpio_line *lines; - - struct gpio_chip chip; -}; -#define gpio_chip_to_gb_gpio_controller(chip) \ - container_of(chip, struct gb_gpio_controller, chip) - -/* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 - -/* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 -#define GB_GPIO_TYPE_LINE_COUNT 0x02 -#define GB_GPIO_TYPE_ACTIVATE 0x03 -#define GB_GPIO_TYPE_DEACTIVATE 0x04 -#define GB_GPIO_TYPE_GET_DIRECTION 0x05 -#define GB_GPIO_TYPE_DIRECTION_IN 0x06 -#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 -#define GB_GPIO_TYPE_GET_VALUE 0x08 -#define GB_GPIO_TYPE_SET_VALUE 0x09 -#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a -#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ - -/* version request has no payload */ -struct gb_gpio_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* line count request has no payload */ -struct gb_gpio_line_count_response { - __u8 count; -}; - -struct gb_gpio_activate_request { - __u8 which; -}; -/* activate response has no payload */ - -struct gb_gpio_deactivate_request { - __u8 which; -}; -/* deactivate response has no payload */ - -struct gb_gpio_get_direction_request { - __u8 which; -}; -struct gb_gpio_get_direction_response { - __u8 direction; -}; - -struct gb_gpio_direction_in_request { - __u8 which; -}; -/* direction in response has no payload */ - -struct gb_gpio_direction_out_request { - __u8 which; - __u8 value; -}; -/* direction out response has no payload */ - -struct gb_gpio_get_value_request { - __u8 which; -}; -struct gb_gpio_get_value_response { - __u8 value; -}; - -struct gb_gpio_set_value_request { - __u8 which; - __u8 value; -}; -/* set value response has no payload */ - -struct gb_gpio_set_debounce_request { - __u8 which; - __le16 usec; -}; -/* debounce response has no payload */ - - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) -{ - struct gb_gpio_proto_version_response response; - int ret; - - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_GPIO_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_GPIO_VERSION_MAJOR); - return -ENOTSUPP; - } - ggc->version_major = response.major; - ggc->version_minor = response.minor; - return 0; -} - -static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) -{ - struct gb_gpio_line_count_response response; - int ret; - - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, - NULL, 0, &response, sizeof(response)); - if (!ret) - ggc->line_max = response.count; - return ret; -} - -static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) -{ - struct gb_gpio_activate_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = true; - return ret; -} - -static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_deactivate_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = false; - return ret; -} - -static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_get_direction_request request; - struct gb_gpio_get_direction_response response; - int ret; - u8 direction; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, - &request, sizeof(request), - &response, sizeof(response)); - if (ret) - return ret; - - direction = response.direction; - if (direction && direction != 1) - pr_warn("gpio %u direction was %u (should be 0 or 1)\n", - which, direction); - ggc->lines[which].direction = direction ? 1 : 0; - return 0; -} - -static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_direction_in_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].direction = 1; - return ret; -} - -static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, - u8 which, bool value_high) -{ - struct gb_gpio_direction_out_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.value = value_high ? 1 : 0; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].direction = 0; - return ret; -} - -static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_get_value_request request; - struct gb_gpio_get_value_response response; - int ret; - u8 value; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, - &request, sizeof(request), - &response, sizeof(response)); - if (ret) - return ret; - - value = response.value; - if (value && value != 1) - pr_warn("gpio %u value was %u (should be 0 or 1)\n", - which, value); - ggc->lines[which].value = value ? 1 : 0; - return 0; -} - -static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, - u8 which, bool value_high) -{ - struct gb_gpio_set_value_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.value = value_high ? 1 : 0; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, - &request, sizeof(request), NULL, 0); - if (!ret) { - /* XXX should this set direction to out? */ - ggc->lines[which].value = request.value; - } - return ret; -} - -static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, - u8 which, u16 debounce_usec) -{ - struct gb_gpio_set_debounce_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.usec = cpu_to_le16(debounce_usec); - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].debounce_usec = debounce_usec; - return ret; -} - -static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; -} - -static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } - ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ -} - -static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - which = (u8)offset; - ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); - if (ret) - ; /* return ret; */ - return gb_gpio_controller->lines[which].direction ? 1 : 0; -} - -static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; -} - -static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); - if (ret) - ; /* return ret; */ - return 0; -} - -static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - which = (u8)offset; - ret = gb_gpio_get_value_operation(gb_gpio_controller, which); - if (ret) - return ret; - return (int)gb_gpio_controller->lines[which].value; -} - -static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset < 0 || offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } - ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); - if (ret) - ; /* return ret; */ -} - -static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, - unsigned debounce) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u16 usec; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - if (debounce > (unsigned int)U16_MAX) - return -EINVAL; - usec = (u8)debounce; - ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); - if (ret) - ; /* return ret; */ - - return 0; /* XXX */ -} - -static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return 0; /* XXX */ -} - -static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - return; /* XXX */ -} - -static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) -{ - u32 line_count; - size_t size; - int ret; - - /* First thing we need to do is check the version */ - ret = gb_gpio_proto_version_operation(gb_gpio_controller); - if (ret) - ; /* return ret; */ - - /* Now find out how many lines there are */ - ret = gb_gpio_line_count_operation(gb_gpio_controller); - if (ret) - ; /* return ret; */ - line_count = (u32)gb_gpio_controller->line_max + 1; - size = line_count * sizeof(*gb_gpio_controller->lines); - gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); - if (!gb_gpio_controller->lines) - return -ENOMEM; - - return ret; -} - -static int gb_gpio_connection_init(struct gb_connection *connection) -{ - struct gb_gpio_controller *gb_gpio_controller; - struct gpio_chip *gpio; - int ret; - - gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); - if (!gb_gpio_controller) - return -ENOMEM; - gb_gpio_controller->connection = connection; - connection->private = gb_gpio_controller; - - ret = gb_gpio_controller_setup(gb_gpio_controller); - if (ret) - goto out_err; - - gpio = &gb_gpio_controller->chip; - - gpio->label = "greybus_gpio"; - gpio->owner = THIS_MODULE; /* XXX Module get? */ - - gpio->request = gb_gpio_request; - gpio->free = gb_gpio_free; - gpio->get_direction = gb_gpio_get_direction; - gpio->direction_input = gb_gpio_direction_input; - gpio->direction_output = gb_gpio_direction_output; - gpio->get = gb_gpio_get; - gpio->set = gb_gpio_set; - gpio->set_debounce = gb_gpio_set_debounce; - gpio->to_irq = gb_gpio_to_irq; - gpio->dbg_show = gb_gpio_dbg_show; - - gpio->base = -1; /* Allocate base dynamically */ - gpio->ngpio = gb_gpio_controller->line_max + 1; - gpio->can_sleep = true; /* XXX */ - - ret = gpiochip_add(gpio); - if (ret) { - pr_err("Failed to register GPIO\n"); - return ret; - } - - return 0; -out_err: - kfree(gb_gpio_controller); - return ret; -} - -static void gb_gpio_connection_exit(struct gb_connection *connection) -{ - struct gb_gpio_controller *gb_gpio_controller = connection->private; - - if (!gb_gpio_controller) - return; - - gb_gpiochip_remove(&gb_gpio_controller->chip); - /* kref_put(gb_gpio_controller->connection) */ - kfree(gb_gpio_controller); -} - -static struct gb_protocol gpio_protocol = { - .name = "gpio", - .id = GREYBUS_PROTOCOL_GPIO, - .major = 0, - .minor = 1, - .connection_init = gb_gpio_connection_init, - .connection_exit = gb_gpio_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_gpio_protocol_init(void) -{ - return gb_protocol_register(&gpio_protocol); -} - -void gb_gpio_protocol_exit(void) -{ - gb_protocol_deregister(&gpio_protocol); -} diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c new file mode 100644 index 000000000000..4997588e2617 --- /dev/null +++ b/drivers/staging/greybus/gpio.c @@ -0,0 +1,544 @@ +/* + * GPIO Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_gpio_line { + /* The following has to be an array of line_max entries */ + /* --> make them just a flags field */ + u8 active: 1, + direction: 1, /* 0 = output, 1 = input */ + value: 1; /* 0 = low, 1 = high */ + u16 debounce_usec; +}; + +struct gb_gpio_controller { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 line_max; /* max line number */ + struct gb_gpio_line *lines; + + struct gpio_chip chip; +}; +#define gpio_chip_to_gb_gpio_controller(chip) \ + container_of(chip, struct gb_gpio_controller, chip) + +/* Version of the Greybus GPIO protocol we support */ +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 + +/* Greybus GPIO request types */ +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ + +/* version request has no payload */ +struct gb_gpio_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* line count request has no payload */ +struct gb_gpio_line_count_response { + __u8 count; +}; + +struct gb_gpio_activate_request { + __u8 which; +}; +/* activate response has no payload */ + +struct gb_gpio_deactivate_request { + __u8 which; +}; +/* deactivate response has no payload */ + +struct gb_gpio_get_direction_request { + __u8 which; +}; +struct gb_gpio_get_direction_response { + __u8 direction; +}; + +struct gb_gpio_direction_in_request { + __u8 which; +}; +/* direction in response has no payload */ + +struct gb_gpio_direction_out_request { + __u8 which; + __u8 value; +}; +/* direction out response has no payload */ + +struct gb_gpio_get_value_request { + __u8 which; +}; +struct gb_gpio_get_value_response { + __u8 value; +}; + +struct gb_gpio_set_value_request { + __u8 which; + __u8 value; +}; +/* set value response has no payload */ + +struct gb_gpio_set_debounce_request { + __u8 which; + __le16 usec; +}; +/* debounce response has no payload */ + + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) +{ + struct gb_gpio_proto_version_response response; + int ret; + + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_GPIO_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_GPIO_VERSION_MAJOR); + return -ENOTSUPP; + } + ggc->version_major = response.major; + ggc->version_minor = response.minor; + return 0; +} + +static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) +{ + struct gb_gpio_line_count_response response; + int ret; + + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, + NULL, 0, &response, sizeof(response)); + if (!ret) + ggc->line_max = response.count; + return ret; +} + +static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) +{ + struct gb_gpio_activate_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = true; + return ret; +} + +static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_deactivate_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = false; + return ret; +} + +static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_get_direction_request request; + struct gb_gpio_get_direction_response response; + int ret; + u8 direction; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; + + direction = response.direction; + if (direction && direction != 1) + pr_warn("gpio %u direction was %u (should be 0 or 1)\n", + which, direction); + ggc->lines[which].direction = direction ? 1 : 0; + return 0; +} + +static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_direction_in_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 1; + return ret; +} + +static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, + u8 which, bool value_high) +{ + struct gb_gpio_direction_out_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 0; + return ret; +} + +static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_get_value_request request; + struct gb_gpio_get_value_response response; + int ret; + u8 value; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; + + value = response.value; + if (value && value != 1) + pr_warn("gpio %u value was %u (should be 0 or 1)\n", + which, value); + ggc->lines[which].value = value ? 1 : 0; + return 0; +} + +static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, + u8 which, bool value_high) +{ + struct gb_gpio_set_value_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, + &request, sizeof(request), NULL, 0); + if (!ret) { + /* XXX should this set direction to out? */ + ggc->lines[which].value = request.value; + } + return ret; +} + +static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, + u8 which, u16 debounce_usec) +{ + struct gb_gpio_set_debounce_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.usec = cpu_to_le16(debounce_usec); + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].debounce_usec = debounce_usec; + return ret; +} + +static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ + return 0; +} + +static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); + return; + } + ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ +} + +static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + which = (u8)offset; + ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ + return gb_gpio_controller->lines[which].direction ? 1 : 0; +} + +static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ + return 0; +} + +static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); + if (ret) + ; /* return ret; */ + return 0; +} + +static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + which = (u8)offset; + ret = gb_gpio_get_value_operation(gb_gpio_controller, which); + if (ret) + return ret; + return (int)gb_gpio_controller->lines[which].value; +} + +static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset < 0 || offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); + return; + } + ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); + if (ret) + ; /* return ret; */ +} + +static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u16 usec; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + if (debounce > (unsigned int)U16_MAX) + return -EINVAL; + usec = (u8)debounce; + ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); + if (ret) + ; /* return ret; */ + + return 0; /* XXX */ +} + +static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= chip->ngpio) + return -EINVAL; + + return 0; /* XXX */ +} + +static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + return; /* XXX */ +} + +static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) +{ + u32 line_count; + size_t size; + int ret; + + /* First thing we need to do is check the version */ + ret = gb_gpio_proto_version_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + + /* Now find out how many lines there are */ + ret = gb_gpio_line_count_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + line_count = (u32)gb_gpio_controller->line_max + 1; + size = line_count * sizeof(*gb_gpio_controller->lines); + gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); + if (!gb_gpio_controller->lines) + return -ENOMEM; + + return ret; +} + +static int gb_gpio_connection_init(struct gb_connection *connection) +{ + struct gb_gpio_controller *gb_gpio_controller; + struct gpio_chip *gpio; + int ret; + + gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); + if (!gb_gpio_controller) + return -ENOMEM; + gb_gpio_controller->connection = connection; + connection->private = gb_gpio_controller; + + ret = gb_gpio_controller_setup(gb_gpio_controller); + if (ret) + goto out_err; + + gpio = &gb_gpio_controller->chip; + + gpio->label = "greybus_gpio"; + gpio->owner = THIS_MODULE; /* XXX Module get? */ + + gpio->request = gb_gpio_request; + gpio->free = gb_gpio_free; + gpio->get_direction = gb_gpio_get_direction; + gpio->direction_input = gb_gpio_direction_input; + gpio->direction_output = gb_gpio_direction_output; + gpio->get = gb_gpio_get; + gpio->set = gb_gpio_set; + gpio->set_debounce = gb_gpio_set_debounce; + gpio->to_irq = gb_gpio_to_irq; + gpio->dbg_show = gb_gpio_dbg_show; + + gpio->base = -1; /* Allocate base dynamically */ + gpio->ngpio = gb_gpio_controller->line_max + 1; + gpio->can_sleep = true; /* XXX */ + + ret = gpiochip_add(gpio); + if (ret) { + pr_err("Failed to register GPIO\n"); + return ret; + } + + return 0; +out_err: + kfree(gb_gpio_controller); + return ret; +} + +static void gb_gpio_connection_exit(struct gb_connection *connection) +{ + struct gb_gpio_controller *gb_gpio_controller = connection->private; + + if (!gb_gpio_controller) + return; + + gb_gpiochip_remove(&gb_gpio_controller->chip); + /* kref_put(gb_gpio_controller->connection) */ + kfree(gb_gpio_controller); +} + +static struct gb_protocol gpio_protocol = { + .name = "gpio", + .id = GREYBUS_PROTOCOL_GPIO, + .major = 0, + .minor = 1, + .connection_init = gb_gpio_connection_init, + .connection_exit = gb_gpio_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_gpio_protocol_init(void) +{ + return gb_protocol_register(&gpio_protocol); +} + +void gb_gpio_protocol_exit(void) +{ + gb_protocol_deregister(&gpio_protocol); +} diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c deleted file mode 100644 index c967ae3161c5..000000000000 --- a/drivers/staging/greybus/i2c-gb.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * I2C bridge driver for the Greybus "generic" I2C module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include - -#include "greybus.h" - -struct gb_i2c_device { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - - u32 functionality; - u16 timeout_msec; - u8 retries; - - struct i2c_adapter adapter; -}; - -/* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 - -/* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 -#define GB_I2C_TYPE_RETRIES 0x04 -#define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ - -/* version request has no payload */ -struct gb_i2c_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* functionality request has no payload */ -struct gb_i2c_functionality_response { - __le32 functionality; -}; - -struct gb_i2c_timeout_request { - __le16 msec; -}; -/* timeout response has no payload */ - -struct gb_i2c_retries_request { - __u8 retries; -}; -/* retries response has no payload */ - -/* - * Outgoing data immediately follows the op count and ops array. - * The data for each write (master -> slave) op in the array is sent - * in order, with no (e.g. pad) bytes separating them. - * - * Short reads cause the entire transfer request to fail So response - * payload consists only of bytes read, and the number of bytes is - * exactly what was specified in the corresponding op. Like - * outgoing data, the incoming data is in order and contiguous. - */ -struct gb_i2c_transfer_op { - __le16 addr; - __le16 flags; - __le16 size; -}; - -struct gb_i2c_transfer_request { - __le16 op_count; - struct gb_i2c_transfer_op ops[0]; /* op_count of these */ -}; -struct gb_i2c_transfer_response { - __u8 data[0]; /* inbound data */ -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) -{ - struct gb_i2c_proto_version_response response; - int ret; - - ret = gb_operation_sync(gb_i2c_dev->connection, - GB_I2C_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_I2C_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_I2C_VERSION_MAJOR); - return -ENOTSUPP; - } - gb_i2c_dev->version_major = response.major; - gb_i2c_dev->version_minor = response.minor; - return 0; -} - -/* - * Map Greybus i2c functionality bits into Linux ones - */ -static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) -{ - return gb_i2c_functionality; /* All bits the same for now */ -} - -static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) -{ - struct gb_i2c_functionality_response response; - u32 functionality; - int ret; - - ret = gb_operation_sync(gb_i2c_dev->connection, - GB_I2C_TYPE_FUNCTIONALITY, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - functionality = le32_to_cpu(response.functionality); - gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); - - return 0; -} - -static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) -{ - struct gb_i2c_timeout_request request; - int ret; - - request.msec = cpu_to_le16(msec); - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, - &request, sizeof(request), NULL, 0); - if (ret) - pr_err("timeout operation failed (%d)\n", ret); - else - gb_i2c_dev->timeout_msec = msec; - - return ret; -} - -static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, - u8 retries) -{ - struct gb_i2c_retries_request request; - int ret; - - request.retries = retries; - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, - &request, sizeof(request), NULL, 0); - if (ret) - pr_err("retries operation failed (%d)\n", ret); - else - gb_i2c_dev->retries = retries; - - return ret; -} - - -/* - * Map Linux i2c_msg flags into Greybus i2c transfer op flags. - */ -static u16 gb_i2c_transfer_op_flags_map(u16 flags) -{ - return flags; /* All flags the same for now */ -} - -static void -gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) -{ - u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); - - op->addr = cpu_to_le16(msg->addr); - op->flags = cpu_to_le16(flags); - op->size = cpu_to_le16(msg->len); -} - -static struct gb_operation * -gb_i2c_transfer_request(struct gb_connection *connection, - struct i2c_msg *msgs, u32 msg_count) -{ - struct gb_i2c_transfer_request *request; - struct gb_operation *operation; - struct gb_i2c_transfer_op *op; - struct i2c_msg *msg; - u32 data_out_size = 0; - u32 data_in_size = 0; - size_t request_size; - void *data; - u16 op_count; - u32 i; - - if (msg_count > (u32)U16_MAX) { - gb_connection_err(connection, "msg_count (%u) too big", - msg_count); - return NULL; - } - op_count = (u16)msg_count; - - /* - * In addition to space for all message descriptors we need - * to have enough to hold all outbound message data. - */ - msg = msgs; - for (i = 0; i < msg_count; i++, msg++) - if (msg->flags & I2C_M_RD) - data_in_size += (u32)msg->len; - else - data_out_size += (u32)msg->len; - - request_size = sizeof(*request); - request_size += msg_count * sizeof(*op); - request_size += data_out_size; - - /* Response consists only of incoming data */ - operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, - request_size, data_in_size); - if (!operation) - return NULL; - - request = operation->request->payload; - request->op_count = cpu_to_le16(op_count); - /* Fill in the ops array */ - op = &request->ops[0]; - msg = msgs; - for (i = 0; i < msg_count; i++) - gb_i2c_fill_transfer_op(op++, msg++); - - if (!data_out_size) - return operation; - - /* Copy over the outgoing data; it starts after the last op */ - data = op; - msg = msgs; - for (i = 0; i < msg_count; i++) { - if (!(msg->flags & I2C_M_RD)) { - memcpy(data, msg->buf, msg->len); - data += msg->len; - } - msg++; - } - - return operation; -} - -static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, - struct gb_i2c_transfer_response *response) -{ - struct i2c_msg *msg = msgs; - u8 *data; - u32 i; - - if (!response) - return; - data = response->data; - for (i = 0; i < msg_count; i++) { - if (msg->flags & I2C_M_RD) { - memcpy(msg->buf, data, msg->len); - data += msg->len; - } - msg++; - } -} - -/* - * Some i2c transfer operations return results that are expected. - */ -static bool gb_i2c_expected_transfer_error(int errno) -{ - return errno == -EAGAIN || errno == -ENODEV; -} - -static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, - struct i2c_msg *msgs, u32 msg_count) -{ - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - int ret; - - operation = gb_i2c_transfer_request(connection, msgs, msg_count); - if (!operation) - return -ENOMEM; - - ret = gb_operation_request_send_sync(operation); - if (!ret) { - struct gb_i2c_transfer_response *response; - - response = operation->response->payload; - gb_i2c_transfer_response(msgs, msg_count, response); - ret = msg_count; - } else if (!gb_i2c_expected_transfer_error(ret)) { - pr_err("transfer operation failed (%d)\n", ret); - } - gb_operation_destroy(operation); - - return ret; -} - -static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int msg_count) -{ - struct gb_i2c_device *gb_i2c_dev; - - gb_i2c_dev = i2c_get_adapdata(adap); - - return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); -} - -#if 0 -/* Later */ -static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, - u16 addr, unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data) -{ - struct gb_i2c_device *gb_i2c_dev; - - gb_i2c_dev = i2c_get_adapdata(adap); - - return 0; -} -#endif - -static u32 gb_i2c_functionality(struct i2c_adapter *adap) -{ - struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); - - return gb_i2c_dev->functionality; -} - -static const struct i2c_algorithm gb_i2c_algorithm = { - .master_xfer = gb_i2c_master_xfer, - /* .smbus_xfer = gb_i2c_smbus_xfer, */ - .functionality = gb_i2c_functionality, -}; - -/* - * Do initial setup of the i2c device. This includes verifying we - * can support it (based on the protocol version it advertises). - * If that's OK, we get and cached its functionality bits, and - * set up the retry count and timeout. - * - * Note: gb_i2c_dev->connection is assumed to have been valid. - */ -static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) -{ - int ret; - - /* First thing we need to do is check the version */ - ret = gb_i2c_proto_version_operation(gb_i2c_dev); - if (ret) - return ret; - - /* Assume the functionality never changes, just get it once */ - ret = gb_i2c_functionality_operation(gb_i2c_dev); - if (ret) - return ret; - - /* Set up our default retry count and timeout */ - ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); - if (ret) - return ret; - - return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); -} - -static int gb_i2c_connection_init(struct gb_connection *connection) -{ - struct gb_i2c_device *gb_i2c_dev; - struct i2c_adapter *adapter; - int ret; - - gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); - if (!gb_i2c_dev) - return -ENOMEM; - - gb_i2c_dev->connection = connection; /* refcount? */ - connection->private = gb_i2c_dev; - - ret = gb_i2c_device_setup(gb_i2c_dev); - if (ret) - goto out_err; - - /* Looks good; up our i2c adapter */ - adapter = &gb_i2c_dev->adapter; - adapter->owner = THIS_MODULE; - adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - adapter->algo = &gb_i2c_algorithm; - /* adapter->algo_data = what? */ - adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; - adapter->retries = gb_i2c_dev->retries; - - adapter->dev.parent = &connection->dev; - snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); - i2c_set_adapdata(adapter, gb_i2c_dev); - - ret = i2c_add_adapter(adapter); - if (ret) - goto out_err; - - return 0; -out_err: - /* kref_put(gb_i2c_dev->connection) */ - kfree(gb_i2c_dev); - - return ret; -} - -static void gb_i2c_connection_exit(struct gb_connection *connection) -{ - struct gb_i2c_device *gb_i2c_dev = connection->private; - - i2c_del_adapter(&gb_i2c_dev->adapter); - /* kref_put(gb_i2c_dev->connection) */ - kfree(gb_i2c_dev); -} - -static struct gb_protocol i2c_protocol = { - .name = "i2c", - .id = GREYBUS_PROTOCOL_I2C, - .major = 0, - .minor = 1, - .connection_init = gb_i2c_connection_init, - .connection_exit = gb_i2c_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_i2c_protocol_init(void) -{ - return gb_protocol_register(&i2c_protocol); -} - -void gb_i2c_protocol_exit(void) -{ - gb_protocol_deregister(&i2c_protocol); -} diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c new file mode 100644 index 000000000000..c967ae3161c5 --- /dev/null +++ b/drivers/staging/greybus/i2c.c @@ -0,0 +1,450 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include + +#include "greybus.h" + +struct gb_i2c_device { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + u32 functionality; + u16 timeout_msec; + u8 retries; + + struct i2c_adapter adapter; +}; + +/* Version of the Greybus i2c protocol we support */ +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 + +/* Greybus i2c request types */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 +#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ + +/* version request has no payload */ +struct gb_i2c_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* functionality request has no payload */ +struct gb_i2c_functionality_response { + __le32 functionality; +}; + +struct gb_i2c_timeout_request { + __le16 msec; +}; +/* timeout response has no payload */ + +struct gb_i2c_retries_request { + __u8 retries; +}; +/* retries response has no payload */ + +/* + * Outgoing data immediately follows the op count and ops array. + * The data for each write (master -> slave) op in the array is sent + * in order, with no (e.g. pad) bytes separating them. + * + * Short reads cause the entire transfer request to fail So response + * payload consists only of bytes read, and the number of bytes is + * exactly what was specified in the corresponding op. Like + * outgoing data, the incoming data is in order and contiguous. + */ +struct gb_i2c_transfer_op { + __le16 addr; + __le16 flags; + __le16 size; +}; + +struct gb_i2c_transfer_request { + __le16 op_count; + struct gb_i2c_transfer_op ops[0]; /* op_count of these */ +}; +struct gb_i2c_transfer_response { + __u8 data[0]; /* inbound data */ +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_i2c_proto_version_response response; + int ret; + + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_I2C_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_I2C_VERSION_MAJOR); + return -ENOTSUPP; + } + gb_i2c_dev->version_major = response.major; + gb_i2c_dev->version_minor = response.minor; + return 0; +} + +/* + * Map Greybus i2c functionality bits into Linux ones + */ +static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) +{ + return gb_i2c_functionality; /* All bits the same for now */ +} + +static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_i2c_functionality_response response; + u32 functionality; + int ret; + + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_FUNCTIONALITY, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + functionality = le32_to_cpu(response.functionality); + gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); + + return 0; +} + +static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) +{ + struct gb_i2c_timeout_request request; + int ret; + + request.msec = cpu_to_le16(msec); + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("timeout operation failed (%d)\n", ret); + else + gb_i2c_dev->timeout_msec = msec; + + return ret; +} + +static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, + u8 retries) +{ + struct gb_i2c_retries_request request; + int ret; + + request.retries = retries; + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("retries operation failed (%d)\n", ret); + else + gb_i2c_dev->retries = retries; + + return ret; +} + + +/* + * Map Linux i2c_msg flags into Greybus i2c transfer op flags. + */ +static u16 gb_i2c_transfer_op_flags_map(u16 flags) +{ + return flags; /* All flags the same for now */ +} + +static void +gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) +{ + u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); + + op->addr = cpu_to_le16(msg->addr); + op->flags = cpu_to_le16(flags); + op->size = cpu_to_le16(msg->len); +} + +static struct gb_operation * +gb_i2c_transfer_request(struct gb_connection *connection, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_i2c_transfer_request *request; + struct gb_operation *operation; + struct gb_i2c_transfer_op *op; + struct i2c_msg *msg; + u32 data_out_size = 0; + u32 data_in_size = 0; + size_t request_size; + void *data; + u16 op_count; + u32 i; + + if (msg_count > (u32)U16_MAX) { + gb_connection_err(connection, "msg_count (%u) too big", + msg_count); + return NULL; + } + op_count = (u16)msg_count; + + /* + * In addition to space for all message descriptors we need + * to have enough to hold all outbound message data. + */ + msg = msgs; + for (i = 0; i < msg_count; i++, msg++) + if (msg->flags & I2C_M_RD) + data_in_size += (u32)msg->len; + else + data_out_size += (u32)msg->len; + + request_size = sizeof(*request); + request_size += msg_count * sizeof(*op); + request_size += data_out_size; + + /* Response consists only of incoming data */ + operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, + request_size, data_in_size); + if (!operation) + return NULL; + + request = operation->request->payload; + request->op_count = cpu_to_le16(op_count); + /* Fill in the ops array */ + op = &request->ops[0]; + msg = msgs; + for (i = 0; i < msg_count; i++) + gb_i2c_fill_transfer_op(op++, msg++); + + if (!data_out_size) + return operation; + + /* Copy over the outgoing data; it starts after the last op */ + data = op; + msg = msgs; + for (i = 0; i < msg_count; i++) { + if (!(msg->flags & I2C_M_RD)) { + memcpy(data, msg->buf, msg->len); + data += msg->len; + } + msg++; + } + + return operation; +} + +static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, + struct gb_i2c_transfer_response *response) +{ + struct i2c_msg *msg = msgs; + u8 *data; + u32 i; + + if (!response) + return; + data = response->data; + for (i = 0; i < msg_count; i++) { + if (msg->flags & I2C_M_RD) { + memcpy(msg->buf, data, msg->len); + data += msg->len; + } + msg++; + } +} + +/* + * Some i2c transfer operations return results that are expected. + */ +static bool gb_i2c_expected_transfer_error(int errno) +{ + return errno == -EAGAIN || errno == -ENODEV; +} + +static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + int ret; + + operation = gb_i2c_transfer_request(connection, msgs, msg_count); + if (!operation) + return -ENOMEM; + + ret = gb_operation_request_send_sync(operation); + if (!ret) { + struct gb_i2c_transfer_response *response; + + response = operation->response->payload; + gb_i2c_transfer_response(msgs, msg_count, response); + ret = msg_count; + } else if (!gb_i2c_expected_transfer_error(ret)) { + pr_err("transfer operation failed (%d)\n", ret); + } + gb_operation_destroy(operation); + + return ret; +} + +static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int msg_count) +{ + struct gb_i2c_device *gb_i2c_dev; + + gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); +} + +#if 0 +/* Later */ +static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct gb_i2c_device *gb_i2c_dev; + + gb_i2c_dev = i2c_get_adapdata(adap); + + return 0; +} +#endif + +static u32 gb_i2c_functionality(struct i2c_adapter *adap) +{ + struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_dev->functionality; +} + +static const struct i2c_algorithm gb_i2c_algorithm = { + .master_xfer = gb_i2c_master_xfer, + /* .smbus_xfer = gb_i2c_smbus_xfer, */ + .functionality = gb_i2c_functionality, +}; + +/* + * Do initial setup of the i2c device. This includes verifying we + * can support it (based on the protocol version it advertises). + * If that's OK, we get and cached its functionality bits, and + * set up the retry count and timeout. + * + * Note: gb_i2c_dev->connection is assumed to have been valid. + */ +static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) +{ + int ret; + + /* First thing we need to do is check the version */ + ret = gb_i2c_proto_version_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Assume the functionality never changes, just get it once */ + ret = gb_i2c_functionality_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Set up our default retry count and timeout */ + ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); + if (ret) + return ret; + + return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); +} + +static int gb_i2c_connection_init(struct gb_connection *connection) +{ + struct gb_i2c_device *gb_i2c_dev; + struct i2c_adapter *adapter; + int ret; + + gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); + if (!gb_i2c_dev) + return -ENOMEM; + + gb_i2c_dev->connection = connection; /* refcount? */ + connection->private = gb_i2c_dev; + + ret = gb_i2c_device_setup(gb_i2c_dev); + if (ret) + goto out_err; + + /* Looks good; up our i2c adapter */ + adapter = &gb_i2c_dev->adapter; + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->algo = &gb_i2c_algorithm; + /* adapter->algo_data = what? */ + adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; + adapter->retries = gb_i2c_dev->retries; + + adapter->dev.parent = &connection->dev; + snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); + i2c_set_adapdata(adapter, gb_i2c_dev); + + ret = i2c_add_adapter(adapter); + if (ret) + goto out_err; + + return 0; +out_err: + /* kref_put(gb_i2c_dev->connection) */ + kfree(gb_i2c_dev); + + return ret; +} + +static void gb_i2c_connection_exit(struct gb_connection *connection) +{ + struct gb_i2c_device *gb_i2c_dev = connection->private; + + i2c_del_adapter(&gb_i2c_dev->adapter); + /* kref_put(gb_i2c_dev->connection) */ + kfree(gb_i2c_dev); +} + +static struct gb_protocol i2c_protocol = { + .name = "i2c", + .id = GREYBUS_PROTOCOL_I2C, + .major = 0, + .minor = 1, + .connection_init = gb_i2c_connection_init, + .connection_exit = gb_i2c_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_i2c_protocol_init(void) +{ + return gb_protocol_register(&i2c_protocol); +} + +void gb_i2c_protocol_exit(void) +{ + gb_protocol_deregister(&i2c_protocol); +} diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c deleted file mode 100644 index 91f7b87a1cae..000000000000 --- a/drivers/staging/greybus/pwm-gb.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * PWM Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_pwm_chip { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - u8 pwm_max; /* max pwm number */ - - struct pwm_chip chip; - struct pwm_chip *pwm; -}; -#define pwm_chip_to_gb_pwm_chip(chip) \ - container_of(chip, struct gb_pwm_chip, chip) - -/* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 - -/* Greybus PWM request types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 -#define GB_PWM_TYPE_PWM_COUNT 0x02 -#define GB_PWM_TYPE_ACTIVATE 0x03 -#define GB_PWM_TYPE_DEACTIVATE 0x04 -#define GB_PWM_TYPE_CONFIG 0x05 -#define GB_PWM_TYPE_POLARITY 0x06 -#define GB_PWM_TYPE_ENABLE 0x07 -#define GB_PWM_TYPE_DISABLE 0x08 -#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -/* version request has no payload */ -struct gb_pwm_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* pwm count request has no payload */ -struct gb_pwm_count_response { - __u8 count; -}; - -struct gb_pwm_activate_request { - __u8 which; -}; - -struct gb_pwm_deactivate_request { - __u8 which; -}; - -struct gb_pwm_config_request { - __u8 which; - __le32 duty; - __le32 period; -}; - -struct gb_pwm_polarity_request { - __u8 which; - __u8 polarity; -}; - -struct gb_pwm_enable_request { - __u8 which; -}; - -struct gb_pwm_disable_request { - __u8 which; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) -{ - struct gb_pwm_proto_version_response response; - int ret; - - ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - - if (ret) - return ret; - - if (response.major > GB_PWM_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_PWM_VERSION_MAJOR); - return -ENOTSUPP; - } - pwmc->version_major = response.major; - pwmc->version_minor = response.minor; - return 0; -} - -static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) -{ - struct gb_pwm_count_response response; - int ret; - - ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - pwmc->pwm_max = response.count; - return 0; -} - -static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_activate_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_deactivate_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, - u8 which, u32 duty, u32 period) -{ - struct gb_pwm_config_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - request.duty = cpu_to_le32(duty); - request.period = cpu_to_le32(period); - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, - &request, sizeof(request), NULL, 0); -} - - -static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, - u8 which, u8 polarity) -{ - struct gb_pwm_polarity_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - request.polarity = polarity; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_enable_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_disable_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_activate_operation(pwmc, pwm->hwpwm); -}; - -static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - if (test_bit(PWMF_ENABLED, &pwm->flags)) - dev_warn(chip->dev, "freeing PWM device without disabling\n"); - - gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); -} - -static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); -}; - -static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); -}; - -static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_enable_operation(pwmc, pwm->hwpwm); -}; - -static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - gb_pwm_disable_operation(pwmc, pwm->hwpwm); -}; - -static const struct pwm_ops gb_pwm_ops = { - .request = gb_pwm_request, - .free = gb_pwm_free, - .config = gb_pwm_config, - .set_polarity = gb_pwm_set_polarity, - .enable = gb_pwm_enable, - .disable = gb_pwm_disable, - .owner = THIS_MODULE, -}; - -static int gb_pwm_connection_init(struct gb_connection *connection) -{ - struct gb_pwm_chip *pwmc; - struct pwm_chip *pwm; - int ret; - - pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); - if (!pwmc) - return -ENOMEM; - pwmc->connection = connection; - connection->private = pwmc; - - /* Check for compatible protocol version */ - ret = gb_pwm_proto_version_operation(pwmc); - if (ret) - goto out_err; - - /* Query number of pwms present */ - ret = gb_pwm_count_operation(pwmc); - if (ret) - goto out_err; - - pwm = &pwmc->chip; - - pwm->dev = &connection->dev; - pwm->ops = &gb_pwm_ops; - pwm->base = -1; /* Allocate base dynamically */ - pwm->npwm = pwmc->pwm_max + 1; - pwm->can_sleep = true; /* FIXME */ - - ret = pwmchip_add(pwm); - if (ret) { - pr_err("Failed to register PWM\n"); - return ret; - } - - return 0; -out_err: - kfree(pwmc); - return ret; -} - -static void gb_pwm_connection_exit(struct gb_connection *connection) -{ - struct gb_pwm_chip *pwmc = connection->private; - - if (!pwmc) - return; - - pwmchip_remove(&pwmc->chip); - /* kref_put(pwmc->connection) */ - kfree(pwmc); -} - -static struct gb_protocol pwm_protocol = { - .name = "pwm", - .id = GREYBUS_PROTOCOL_PWM, - .major = 0, - .minor = 1, - .connection_init = gb_pwm_connection_init, - .connection_exit = gb_pwm_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_pwm_protocol_init(void) -{ - return gb_protocol_register(&pwm_protocol); -} - -void gb_pwm_protocol_exit(void) -{ - gb_protocol_deregister(&pwm_protocol); -} diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c new file mode 100644 index 000000000000..91f7b87a1cae --- /dev/null +++ b/drivers/staging/greybus/pwm.c @@ -0,0 +1,331 @@ +/* + * PWM Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_pwm_chip { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 pwm_max; /* max pwm number */ + + struct pwm_chip chip; + struct pwm_chip *pwm; +}; +#define pwm_chip_to_gb_pwm_chip(chip) \ + container_of(chip, struct gb_pwm_chip, chip) + +/* Version of the Greybus PWM protocol we support */ +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 + +/* Greybus PWM request types */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 +#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +/* version request has no payload */ +struct gb_pwm_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* pwm count request has no payload */ +struct gb_pwm_count_response { + __u8 count; +}; + +struct gb_pwm_activate_request { + __u8 which; +}; + +struct gb_pwm_deactivate_request { + __u8 which; +}; + +struct gb_pwm_config_request { + __u8 which; + __le32 duty; + __le32 period; +}; + +struct gb_pwm_polarity_request { + __u8 which; + __u8 polarity; +}; + +struct gb_pwm_enable_request { + __u8 which; +}; + +struct gb_pwm_disable_request { + __u8 which; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_pwm_proto_version_response response; + int ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + + if (ret) + return ret; + + if (response.major > GB_PWM_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_PWM_VERSION_MAJOR); + return -ENOTSUPP; + } + pwmc->version_major = response.major; + pwmc->version_minor = response.minor; + return 0; +} + +static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_pwm_count_response response; + int ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + pwmc->pwm_max = response.count; + return 0; +} + +static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_activate_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_deactivate_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, + u8 which, u32 duty, u32 period) +{ + struct gb_pwm_config_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + request.duty = cpu_to_le32(duty); + request.period = cpu_to_le32(period); + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, + &request, sizeof(request), NULL, 0); +} + + +static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, + u8 which, u8 polarity) +{ + struct gb_pwm_polarity_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + request.polarity = polarity; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_enable_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_disable_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_activate_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + dev_warn(chip->dev, "freeing PWM device without disabling\n"); + + gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); +} + +static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); +}; + +static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); +}; + +static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_enable_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + gb_pwm_disable_operation(pwmc, pwm->hwpwm); +}; + +static const struct pwm_ops gb_pwm_ops = { + .request = gb_pwm_request, + .free = gb_pwm_free, + .config = gb_pwm_config, + .set_polarity = gb_pwm_set_polarity, + .enable = gb_pwm_enable, + .disable = gb_pwm_disable, + .owner = THIS_MODULE, +}; + +static int gb_pwm_connection_init(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc; + struct pwm_chip *pwm; + int ret; + + pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); + if (!pwmc) + return -ENOMEM; + pwmc->connection = connection; + connection->private = pwmc; + + /* Check for compatible protocol version */ + ret = gb_pwm_proto_version_operation(pwmc); + if (ret) + goto out_err; + + /* Query number of pwms present */ + ret = gb_pwm_count_operation(pwmc); + if (ret) + goto out_err; + + pwm = &pwmc->chip; + + pwm->dev = &connection->dev; + pwm->ops = &gb_pwm_ops; + pwm->base = -1; /* Allocate base dynamically */ + pwm->npwm = pwmc->pwm_max + 1; + pwm->can_sleep = true; /* FIXME */ + + ret = pwmchip_add(pwm); + if (ret) { + pr_err("Failed to register PWM\n"); + return ret; + } + + return 0; +out_err: + kfree(pwmc); + return ret; +} + +static void gb_pwm_connection_exit(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc = connection->private; + + if (!pwmc) + return; + + pwmchip_remove(&pwmc->chip); + /* kref_put(pwmc->connection) */ + kfree(pwmc); +} + +static struct gb_protocol pwm_protocol = { + .name = "pwm", + .id = GREYBUS_PROTOCOL_PWM, + .major = 0, + .minor = 1, + .connection_init = gb_pwm_connection_init, + .connection_exit = gb_pwm_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_pwm_protocol_init(void) +{ + return gb_protocol_register(&pwm_protocol); +} + +void gb_pwm_protocol_exit(void) +{ + gb_protocol_deregister(&pwm_protocol); +} diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c deleted file mode 100644 index d324846d09ab..000000000000 --- a/drivers/staging/greybus/sdio-gb.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SD/MMC Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include - -#include "greybus.h" - -struct gb_sdio_host { - struct gb_connection *connection; - struct mmc_host *mmc; - struct mmc_request *mrq; - // FIXME - some lock? -}; - -static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - // FIXME - do something here... -} - -static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - // FIXME - do something here... -} - -static int gb_sd_get_ro(struct mmc_host *mmc) -{ - // FIXME - do something here... - return 0; -} - -static const struct mmc_host_ops gb_sd_ops = { - .request = gb_sd_request, - .set_ios = gb_sd_set_ios, - .get_ro = gb_sd_get_ro, -}; - -static int gb_sdio_connection_init(struct gb_connection *connection) -{ - struct mmc_host *mmc; - struct gb_sdio_host *host; - - mmc = mmc_alloc_host(sizeof(*host), &connection->dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - mmc->ops = &gb_sd_ops; - // FIXME - set up size limits we can handle. - // FIXME - register the host controller. - - host->connection = connection; - connection->private = host; - return 0; -} - -static void gb_sdio_connection_exit(struct gb_connection *connection) -{ - struct mmc_host *mmc; - struct gb_sdio_host *host; - - host = connection->private; - if (!host) - return; - - mmc = host->mmc; - mmc_remove_host(mmc); - mmc_free_host(mmc); - connection->private = NULL; -} - -static struct gb_protocol sdio_protocol = { - .name = "sdio", - .id = GREYBUS_PROTOCOL_SDIO, - .major = 0, - .minor = 1, - .connection_init = gb_sdio_connection_init, - .connection_exit = gb_sdio_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_sdio_protocol_init(void) -{ - return gb_protocol_register(&sdio_protocol); -} - -void gb_sdio_protocol_exit(void) -{ - gb_protocol_deregister(&sdio_protocol); -} diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c new file mode 100644 index 000000000000..d324846d09ab --- /dev/null +++ b/drivers/staging/greybus/sdio.c @@ -0,0 +1,99 @@ +/* + * SD/MMC Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include + +#include "greybus.h" + +struct gb_sdio_host { + struct gb_connection *connection; + struct mmc_host *mmc; + struct mmc_request *mrq; + // FIXME - some lock? +}; + +static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + // FIXME - do something here... +} + +static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + // FIXME - do something here... +} + +static int gb_sd_get_ro(struct mmc_host *mmc) +{ + // FIXME - do something here... + return 0; +} + +static const struct mmc_host_ops gb_sd_ops = { + .request = gb_sd_request, + .set_ios = gb_sd_set_ios, + .get_ro = gb_sd_get_ro, +}; + +static int gb_sdio_connection_init(struct gb_connection *connection) +{ + struct mmc_host *mmc; + struct gb_sdio_host *host; + + mmc = mmc_alloc_host(sizeof(*host), &connection->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + mmc->ops = &gb_sd_ops; + // FIXME - set up size limits we can handle. + // FIXME - register the host controller. + + host->connection = connection; + connection->private = host; + return 0; +} + +static void gb_sdio_connection_exit(struct gb_connection *connection) +{ + struct mmc_host *mmc; + struct gb_sdio_host *host; + + host = connection->private; + if (!host) + return; + + mmc = host->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + connection->private = NULL; +} + +static struct gb_protocol sdio_protocol = { + .name = "sdio", + .id = GREYBUS_PROTOCOL_SDIO, + .major = 0, + .minor = 1, + .connection_init = gb_sdio_connection_init, + .connection_exit = gb_sdio_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_sdio_protocol_init(void) +{ + return gb_protocol_register(&sdio_protocol); +} + +void gb_sdio_protocol_exit(void) +{ + gb_protocol_deregister(&sdio_protocol); +} diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c deleted file mode 100644 index 032062019d20..000000000000 --- a/drivers/staging/greybus/uart-gb.c +++ /dev/null @@ -1,787 +0,0 @@ -/* - * UART driver for the Greybus "generic" UART module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - * - * Heavily based on drivers/usb/class/cdc-acm.c and - * drivers/usb/serial/usb-serial.c. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" - -#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ -#define GB_NAME "ttyGB" - -/* Version of the Greybus PWM protocol we support */ -#define GB_UART_VERSION_MAJOR 0x00 -#define GB_UART_VERSION_MINOR 0x01 - -/* Greybus UART request types */ -#define GB_UART_REQ_INVALID 0x00 -#define GB_UART_REQ_PROTOCOL_VERSION 0x01 -#define GB_UART_REQ_SEND_DATA 0x02 -#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ -#define GB_UART_REQ_SET_LINE_CODING 0x04 -#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 -#define GB_UART_REQ_SET_BREAK 0x06 -#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ -#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -struct gb_uart_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_uart_send_data_request { - __le16 size; - __u8 data[0]; -}; - -struct gb_serial_line_coding { - __le32 rate; - __u8 format; -#define GB_SERIAL_1_STOP_BITS 0 -#define GB_SERIAL_1_5_STOP_BITS 1 -#define GB_SERIAL_2_STOP_BITS 2 - - __u8 parity; -#define GB_SERIAL_NO_PARITY 0 -#define GB_SERIAL_ODD_PARITY 1 -#define GB_SERIAL_EVEN_PARITY 2 -#define GB_SERIAL_MARK_PARITY 3 -#define GB_SERIAL_SPACE_PARITY 4 - - __u8 data; -} __attribute__ ((packed)); - -struct gb_uart_set_line_coding_request { - struct gb_serial_line_coding line_coding; -}; - -/* output control lines */ -#define GB_UART_CTRL_DTR 0x01 -#define GB_UART_CTRL_RTS 0x02 - -struct gb_uart_set_control_line_state_request { - __le16 control; -}; - -struct gb_uart_set_break_request { - __u8 state; -}; - -/* input control lines and line errors */ -#define GB_UART_CTRL_DCD 0x01 -#define GB_UART_CTRL_DSR 0x02 -#define GB_UART_CTRL_BRK 0x04 -#define GB_UART_CTRL_RI 0x08 - -#define GB_UART_CTRL_FRAMING 0x10 -#define GB_UART_CTRL_PARITY 0x20 -#define GB_UART_CTRL_OVERRUN 0x40 - -struct gb_uart_serial_state_request { - __u16 control; -}; - -struct gb_tty { - struct tty_port port; - struct gb_connection *connection; - u16 cport_id; - unsigned int minor; - unsigned char clocal; - bool disconnected; - spinlock_t read_lock; - spinlock_t write_lock; - struct async_icount iocount; - struct async_icount oldcount; - wait_queue_head_t wioctl; - struct mutex mutex; - u8 version_major; - u8 version_minor; - unsigned int ctrlin; /* input control lines */ - unsigned int ctrlout; /* output control lines */ - struct gb_serial_line_coding line_coding; -}; - - -static struct tty_driver *gb_tty_driver; -static DEFINE_IDR(tty_minors); -static DEFINE_MUTEX(table_lock); -static atomic_t reference_count = ATOMIC_INIT(0); - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_tty *tty) -{ - struct gb_uart_proto_version_response response; - int ret; - - ret = gb_operation_sync(tty->connection, - GB_UART_REQ_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_UART_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_UART_VERSION_MAJOR); - return -ENOTSUPP; - } - tty->version_major = response.major; - tty->version_minor = response.minor; - - pr_debug("%s: version_major = %u version_minor = %u\n", - __func__, tty->version_major, tty->version_minor); - return 0; -} - -static int send_data(struct gb_tty *tty, u16 size, const u8 *data) -{ - struct gb_uart_send_data_request *request; - int retval; - - if (!data || !size) - return 0; - - request = kmalloc(sizeof(*request) + size, GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->size = cpu_to_le16(size); - memcpy(&request->data[0], data, size); - retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, - request, sizeof(*request) + size, NULL, 0); - - kfree(request); - return retval; -} - -static int send_line_coding(struct gb_tty *tty) -{ - struct gb_uart_set_line_coding_request request; - - memcpy(&request.line_coding, &tty->line_coding, - sizeof(tty->line_coding)); - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, - &request, sizeof(request), NULL, 0); -} - -static int send_control(struct gb_tty *tty, u16 control) -{ - struct gb_uart_set_control_line_state_request request; - - request.control = cpu_to_le16(control); - return gb_operation_sync(tty->connection, - GB_UART_REQ_SET_CONTROL_LINE_STATE, - &request, sizeof(request), NULL, 0); -} - -static int send_break(struct gb_tty *tty, u8 state) -{ - struct gb_uart_set_break_request request; - - if ((state != 0) && (state != 1)) { - dev_err(&tty->connection->dev, - "invalid break state of %d\n", state); - return -EINVAL; - } - - request.state = state; - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, - &request, sizeof(request), NULL, 0); -} - - -static struct gb_tty *get_gb_by_minor(unsigned minor) -{ - struct gb_tty *gb_tty; - - mutex_lock(&table_lock); - gb_tty = idr_find(&tty_minors, minor); - if (gb_tty) { - mutex_lock(&gb_tty->mutex); - if (gb_tty->disconnected) { - mutex_unlock(&gb_tty->mutex); - gb_tty = NULL; - } else { - tty_port_get(&gb_tty->port); - mutex_unlock(&gb_tty->mutex); - } - } - mutex_unlock(&table_lock); - return gb_tty; -} - -static int alloc_minor(struct gb_tty *gb_tty) -{ - int minor; - - mutex_lock(&table_lock); - minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); - mutex_unlock(&table_lock); - if (minor >= 0) - gb_tty->minor = minor; - return minor; -} - -static void release_minor(struct gb_tty *gb_tty) -{ - int minor = gb_tty->minor; - - gb_tty->minor = 0; /* Maybe should use an invalid value instead */ - mutex_lock(&table_lock); - idr_remove(&tty_minors, minor); - mutex_unlock(&table_lock); -} - -static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct gb_tty *gb_tty; - int retval; - - gb_tty = get_gb_by_minor(tty->index); - if (!gb_tty) - return -ENODEV; - - retval = tty_standard_install(driver, tty); - if (retval) - goto error; - - tty->driver_data = gb_tty; - return 0; -error: - tty_port_put(&gb_tty->port); - return retval; -} - -static int gb_tty_open(struct tty_struct *tty, struct file *file) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return tty_port_open(&gb_tty->port, tty, file); -} - -static void gb_tty_close(struct tty_struct *tty, struct file *file) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_close(&gb_tty->port, tty, file); -} - -static void gb_tty_cleanup(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_put(&gb_tty->port); -} - -static void gb_tty_hangup(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_hangup(&gb_tty->port); -} - -static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return send_data(gb_tty, count, buf); -} - -static int gb_tty_write_room(struct tty_struct *tty) -{ -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - how much do we want to say we have room for? - return 0; -} - -static int gb_tty_chars_in_buffer(struct tty_struct *tty) -{ -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - how many left to send? - return 0; -} - -static int gb_tty_break_ctl(struct tty_struct *tty, int state) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return send_break(gb_tty, state ? 1 : 0); -} - -static void gb_tty_set_termios(struct tty_struct *tty, - struct ktermios *termios_old) -{ - struct gb_tty *gb_tty = tty->driver_data; - struct ktermios *termios = &tty->termios; - struct gb_serial_line_coding newline; - int newctrl = gb_tty->ctrlout; - - newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); - newline.format = termios->c_cflag & CSTOPB ? 2 : 0; - newline.parity = termios->c_cflag & PARENB ? - (termios->c_cflag & PARODD ? 1 : 2) + - (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - - switch (termios->c_cflag & CSIZE) { - case CS5: - newline.data = 5; - break; - case CS6: - newline.data = 6; - break; - case CS7: - newline.data = 7; - break; - case CS8: - default: - newline.data = 8; - break; - } - - /* FIXME: needs to clear unsupported bits in the termios */ - gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); - - if (C_BAUD(tty) == B0) { - newline.rate = gb_tty->line_coding.rate; - newctrl &= GB_UART_CTRL_DTR; - } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { - newctrl |= GB_UART_CTRL_DTR; - } - - if (newctrl != gb_tty->ctrlout) { - gb_tty->ctrlout = newctrl; - send_control(gb_tty, newctrl); - } - - if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { - memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); - send_line_coding(gb_tty); - } -} - -static int gb_tty_tiocmget(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | - (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | - TIOCM_CTS; -} - -static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, - unsigned int clear) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned int newctrl = gb_tty->ctrlout; - - set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | - (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); - clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | - (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); - - newctrl = (newctrl & ~clear) | set; - if (gb_tty->ctrlout == newctrl) - return 0; - - gb_tty->ctrlout = newctrl; - return send_control(gb_tty, newctrl); -} - -static void gb_tty_throttle(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned char stop_char; - int retval; - - if (I_IXOFF(tty)) { - stop_char = STOP_CHAR(tty); - retval = gb_tty_write(tty, &stop_char, 1); - if (retval <= 0) - return; - } - - if (tty->termios.c_cflag & CRTSCTS) { - gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; - retval = send_control(gb_tty, gb_tty->ctrlout); - } - -} - -static void gb_tty_unthrottle(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned char start_char; - int retval; - - if (I_IXOFF(tty)) { - start_char = START_CHAR(tty); - retval = gb_tty_write(tty, &start_char, 1); - if (retval <= 0) - return; - } - - if (tty->termios.c_cflag & CRTSCTS) { - gb_tty->ctrlout |= GB_UART_CTRL_RTS; - retval = send_control(gb_tty, gb_tty->ctrlout); - } -} - -static int get_serial_info(struct gb_tty *gb_tty, - struct serial_struct __user *info) -{ - struct serial_struct tmp; - - if (!info) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; - tmp.type = PORT_16550A; - tmp.line = gb_tty->minor; - tmp.xmit_fifo_size = 16; - tmp.baud_base = 9600; - tmp.close_delay = gb_tty->port.close_delay / 10; - tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; - - if (copy_to_user(info, &tmp, sizeof(tmp))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct gb_tty *gb_tty, - struct serial_struct __user *newinfo) -{ - struct serial_struct new_serial; - unsigned int closing_wait; - unsigned int close_delay; - int retval = 0; - - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - close_delay = new_serial.close_delay * 10; - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; - - mutex_lock(&gb_tty->port.mutex); - if (!capable(CAP_SYS_ADMIN)) { - if ((close_delay != gb_tty->port.close_delay) || - (closing_wait != gb_tty->port.closing_wait)) - retval = -EPERM; - else - retval = -EOPNOTSUPP; - } else { - gb_tty->port.close_delay = close_delay; - gb_tty->port.closing_wait = closing_wait; - } - mutex_unlock(&gb_tty->port.mutex); - return retval; -} - -static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) -{ - int retval = 0; - DECLARE_WAITQUEUE(wait, current); - struct async_icount old; - struct async_icount new; - - if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) - return -EINVAL; - - do { - spin_lock_irq(&gb_tty->read_lock); - old = gb_tty->oldcount; - new = gb_tty->iocount; - gb_tty->oldcount = new; - spin_unlock_irq(&gb_tty->read_lock); - - if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) - break; - if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) - break; - if ((arg & TIOCM_RI) && (old.rng != new.rng)) - break; - - add_wait_queue(&gb_tty->wioctl, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - remove_wait_queue(&gb_tty->wioctl, &wait); - if (gb_tty->disconnected) { - if (arg & TIOCM_CD) - break; - retval = -ENODEV; - } else if (signal_pending(current)) { - retval = -ERESTARTSYS; - } - } while (!retval); - - return retval; -} - -static int get_serial_usage(struct gb_tty *gb_tty, - struct serial_icounter_struct __user *count) -{ - struct serial_icounter_struct icount; - int retval = 0; - - memset(&icount, 0, sizeof(icount)); - icount.dsr = gb_tty->iocount.dsr; - icount.rng = gb_tty->iocount.rng; - icount.dcd = gb_tty->iocount.dcd; - icount.frame = gb_tty->iocount.frame; - icount.overrun = gb_tty->iocount.overrun; - icount.parity = gb_tty->iocount.parity; - icount.brk = gb_tty->iocount.brk; - - if (copy_to_user(count, &icount, sizeof(icount)) > 0) - retval = -EFAULT; - - return retval; -} - -static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct gb_tty *gb_tty = tty->driver_data; - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(gb_tty, - (struct serial_struct __user *)arg); - case TIOCSSERIAL: - return set_serial_info(gb_tty, - (struct serial_struct __user *)arg); - case TIOCMIWAIT: - return wait_serial_change(gb_tty, arg); - case TIOCGICOUNT: - return get_serial_usage(gb_tty, - (struct serial_icounter_struct __user *)arg); - } - - return -ENOIOCTLCMD; -} - - -static const struct tty_operations gb_ops = { - .install = gb_tty_install, - .open = gb_tty_open, - .close = gb_tty_close, - .cleanup = gb_tty_cleanup, - .hangup = gb_tty_hangup, - .write = gb_tty_write, - .write_room = gb_tty_write_room, - .ioctl = gb_tty_ioctl, - .throttle = gb_tty_throttle, - .unthrottle = gb_tty_unthrottle, - .chars_in_buffer = gb_tty_chars_in_buffer, - .break_ctl = gb_tty_break_ctl, - .set_termios = gb_tty_set_termios, - .tiocmget = gb_tty_tiocmget, - .tiocmset = gb_tty_tiocmset, -}; - - -static int gb_tty_init(void); -static void gb_tty_exit(void); - -static int gb_uart_connection_init(struct gb_connection *connection) -{ - struct gb_tty *gb_tty; - struct device *tty_dev; - int retval; - int minor; - - /* First time here, initialize the tty structures */ - if (atomic_inc_return(&reference_count) == 1) { - retval = gb_tty_init(); - if (retval) { - atomic_dec(&reference_count); - return retval; - } - } - - gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); - if (!gb_tty) - return -ENOMEM; - gb_tty->connection = connection; - connection->private = gb_tty; - - /* Check for compatible protocol version */ - retval = get_version(gb_tty); - if (retval) - goto error_version; - - minor = alloc_minor(gb_tty); - if (minor < 0) { - if (minor == -ENOSPC) { - dev_err(&connection->dev, - "no more free minor numbers\n"); - return -ENODEV; - } - return minor; - } - - gb_tty->minor = minor; - spin_lock_init(&gb_tty->write_lock); - spin_lock_init(&gb_tty->read_lock); - init_waitqueue_head(&gb_tty->wioctl); - mutex_init(&gb_tty->mutex); - - send_control(gb_tty, gb_tty->ctrlout); - - /* initialize the uart to be 9600n81 */ - gb_tty->line_coding.rate = cpu_to_le32(9600); - gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; - gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; - gb_tty->line_coding.data = 8; - send_line_coding(gb_tty); - - tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &connection->dev); - if (IS_ERR(tty_dev)) { - retval = PTR_ERR(tty_dev); - goto error; - } - - return 0; -error: - release_minor(gb_tty); -error_version: - connection->private = NULL; - kfree(gb_tty); - return retval; -} - -static void gb_uart_connection_exit(struct gb_connection *connection) -{ - struct gb_tty *gb_tty = connection->private; - struct tty_struct *tty; - - if (!gb_tty) - return; - - mutex_lock(&gb_tty->mutex); - gb_tty->disconnected = true; - - wake_up_all(&gb_tty->wioctl); - connection->private = NULL; - mutex_unlock(&gb_tty->mutex); - - tty = tty_port_tty_get(&gb_tty->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - /* FIXME - stop all traffic */ - - tty_unregister_device(gb_tty_driver, gb_tty->minor); - - /* FIXME - free transmit / receive buffers */ - - tty_port_put(&gb_tty->port); - - kfree(gb_tty); - - /* If last device is gone, tear down the tty structures */ - if (atomic_dec_return(&reference_count) == 0) - gb_tty_exit(); -} - -static int gb_tty_init(void) -{ - int retval = 0; - - gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); - if (IS_ERR(gb_tty_driver)) { - pr_err("Can not allocate tty driver\n"); - retval = -ENOMEM; - goto fail_unregister_dev; - } - - gb_tty_driver->driver_name = "gb"; - gb_tty_driver->name = GB_NAME; - gb_tty_driver->major = 0; - gb_tty_driver->minor_start = 0; - gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gb_tty_driver->init_termios = tty_std_termios; - gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(gb_tty_driver, &gb_ops); - - retval = tty_register_driver(gb_tty_driver); - if (retval) { - pr_err("Can not register tty driver: %d\n", retval); - goto fail_put_gb_tty; - } - - return 0; - -fail_put_gb_tty: - put_tty_driver(gb_tty_driver); -fail_unregister_dev: - return retval; -} - -static void gb_tty_exit(void) -{ - int major = MAJOR(gb_tty_driver->major); - int minor = gb_tty_driver->minor_start; - - tty_unregister_driver(gb_tty_driver); - put_tty_driver(gb_tty_driver); - unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); -} - -static struct gb_protocol uart_protocol = { - .name = "uart", - .id = GREYBUS_PROTOCOL_UART, - .major = 0, - .minor = 1, - .connection_init = gb_uart_connection_init, - .connection_exit = gb_uart_connection_exit, - .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ -}; - -int gb_uart_protocol_init(void) -{ - return gb_protocol_register(&uart_protocol); -} - -void gb_uart_protocol_exit(void) -{ - gb_protocol_deregister(&uart_protocol); -} diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c new file mode 100644 index 000000000000..032062019d20 --- /dev/null +++ b/drivers/staging/greybus/uart.c @@ -0,0 +1,787 @@ +/* + * UART driver for the Greybus "generic" UART module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + * + * Heavily based on drivers/usb/class/cdc-acm.c and + * drivers/usb/serial/usb-serial.c. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ +#define GB_NAME "ttyGB" + +/* Version of the Greybus PWM protocol we support */ +#define GB_UART_VERSION_MAJOR 0x00 +#define GB_UART_VERSION_MINOR 0x01 + +/* Greybus UART request types */ +#define GB_UART_REQ_INVALID 0x00 +#define GB_UART_REQ_PROTOCOL_VERSION 0x01 +#define GB_UART_REQ_SEND_DATA 0x02 +#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ +#define GB_UART_REQ_SET_LINE_CODING 0x04 +#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 +#define GB_UART_REQ_SET_BREAK 0x06 +#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ +#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_uart_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_uart_send_data_request { + __le16 size; + __u8 data[0]; +}; + +struct gb_serial_line_coding { + __le32 rate; + __u8 format; +#define GB_SERIAL_1_STOP_BITS 0 +#define GB_SERIAL_1_5_STOP_BITS 1 +#define GB_SERIAL_2_STOP_BITS 2 + + __u8 parity; +#define GB_SERIAL_NO_PARITY 0 +#define GB_SERIAL_ODD_PARITY 1 +#define GB_SERIAL_EVEN_PARITY 2 +#define GB_SERIAL_MARK_PARITY 3 +#define GB_SERIAL_SPACE_PARITY 4 + + __u8 data; +} __attribute__ ((packed)); + +struct gb_uart_set_line_coding_request { + struct gb_serial_line_coding line_coding; +}; + +/* output control lines */ +#define GB_UART_CTRL_DTR 0x01 +#define GB_UART_CTRL_RTS 0x02 + +struct gb_uart_set_control_line_state_request { + __le16 control; +}; + +struct gb_uart_set_break_request { + __u8 state; +}; + +/* input control lines and line errors */ +#define GB_UART_CTRL_DCD 0x01 +#define GB_UART_CTRL_DSR 0x02 +#define GB_UART_CTRL_BRK 0x04 +#define GB_UART_CTRL_RI 0x08 + +#define GB_UART_CTRL_FRAMING 0x10 +#define GB_UART_CTRL_PARITY 0x20 +#define GB_UART_CTRL_OVERRUN 0x40 + +struct gb_uart_serial_state_request { + __u16 control; +}; + +struct gb_tty { + struct tty_port port; + struct gb_connection *connection; + u16 cport_id; + unsigned int minor; + unsigned char clocal; + bool disconnected; + spinlock_t read_lock; + spinlock_t write_lock; + struct async_icount iocount; + struct async_icount oldcount; + wait_queue_head_t wioctl; + struct mutex mutex; + u8 version_major; + u8 version_minor; + unsigned int ctrlin; /* input control lines */ + unsigned int ctrlout; /* output control lines */ + struct gb_serial_line_coding line_coding; +}; + + +static struct tty_driver *gb_tty_driver; +static DEFINE_IDR(tty_minors); +static DEFINE_MUTEX(table_lock); +static atomic_t reference_count = ATOMIC_INIT(0); + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_tty *tty) +{ + struct gb_uart_proto_version_response response; + int ret; + + ret = gb_operation_sync(tty->connection, + GB_UART_REQ_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_UART_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_UART_VERSION_MAJOR); + return -ENOTSUPP; + } + tty->version_major = response.major; + tty->version_minor = response.minor; + + pr_debug("%s: version_major = %u version_minor = %u\n", + __func__, tty->version_major, tty->version_minor); + return 0; +} + +static int send_data(struct gb_tty *tty, u16 size, const u8 *data) +{ + struct gb_uart_send_data_request *request; + int retval; + + if (!data || !size) + return 0; + + request = kmalloc(sizeof(*request) + size, GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->size = cpu_to_le16(size); + memcpy(&request->data[0], data, size); + retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, + request, sizeof(*request) + size, NULL, 0); + + kfree(request); + return retval; +} + +static int send_line_coding(struct gb_tty *tty) +{ + struct gb_uart_set_line_coding_request request; + + memcpy(&request.line_coding, &tty->line_coding, + sizeof(tty->line_coding)); + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, + &request, sizeof(request), NULL, 0); +} + +static int send_control(struct gb_tty *tty, u16 control) +{ + struct gb_uart_set_control_line_state_request request; + + request.control = cpu_to_le16(control); + return gb_operation_sync(tty->connection, + GB_UART_REQ_SET_CONTROL_LINE_STATE, + &request, sizeof(request), NULL, 0); +} + +static int send_break(struct gb_tty *tty, u8 state) +{ + struct gb_uart_set_break_request request; + + if ((state != 0) && (state != 1)) { + dev_err(&tty->connection->dev, + "invalid break state of %d\n", state); + return -EINVAL; + } + + request.state = state; + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, + &request, sizeof(request), NULL, 0); +} + + +static struct gb_tty *get_gb_by_minor(unsigned minor) +{ + struct gb_tty *gb_tty; + + mutex_lock(&table_lock); + gb_tty = idr_find(&tty_minors, minor); + if (gb_tty) { + mutex_lock(&gb_tty->mutex); + if (gb_tty->disconnected) { + mutex_unlock(&gb_tty->mutex); + gb_tty = NULL; + } else { + tty_port_get(&gb_tty->port); + mutex_unlock(&gb_tty->mutex); + } + } + mutex_unlock(&table_lock); + return gb_tty; +} + +static int alloc_minor(struct gb_tty *gb_tty) +{ + int minor; + + mutex_lock(&table_lock); + minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); + mutex_unlock(&table_lock); + if (minor >= 0) + gb_tty->minor = minor; + return minor; +} + +static void release_minor(struct gb_tty *gb_tty) +{ + int minor = gb_tty->minor; + + gb_tty->minor = 0; /* Maybe should use an invalid value instead */ + mutex_lock(&table_lock); + idr_remove(&tty_minors, minor); + mutex_unlock(&table_lock); +} + +static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gb_tty *gb_tty; + int retval; + + gb_tty = get_gb_by_minor(tty->index); + if (!gb_tty) + return -ENODEV; + + retval = tty_standard_install(driver, tty); + if (retval) + goto error; + + tty->driver_data = gb_tty; + return 0; +error: + tty_port_put(&gb_tty->port); + return retval; +} + +static int gb_tty_open(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return tty_port_open(&gb_tty->port, tty, file); +} + +static void gb_tty_close(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_close(&gb_tty->port, tty, file); +} + +static void gb_tty_cleanup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_put(&gb_tty->port); +} + +static void gb_tty_hangup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_hangup(&gb_tty->port); +} + +static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return send_data(gb_tty, count, buf); +} + +static int gb_tty_write_room(struct tty_struct *tty) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how much do we want to say we have room for? + return 0; +} + +static int gb_tty_chars_in_buffer(struct tty_struct *tty) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how many left to send? + return 0; +} + +static int gb_tty_break_ctl(struct tty_struct *tty, int state) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return send_break(gb_tty, state ? 1 : 0); +} + +static void gb_tty_set_termios(struct tty_struct *tty, + struct ktermios *termios_old) +{ + struct gb_tty *gb_tty = tty->driver_data; + struct ktermios *termios = &tty->termios; + struct gb_serial_line_coding newline; + int newctrl = gb_tty->ctrlout; + + newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); + newline.format = termios->c_cflag & CSTOPB ? 2 : 0; + newline.parity = termios->c_cflag & PARENB ? + (termios->c_cflag & PARODD ? 1 : 2) + + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: + newline.data = 5; + break; + case CS6: + newline.data = 6; + break; + case CS7: + newline.data = 7; + break; + case CS8: + default: + newline.data = 8; + break; + } + + /* FIXME: needs to clear unsupported bits in the termios */ + gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); + + if (C_BAUD(tty) == B0) { + newline.rate = gb_tty->line_coding.rate; + newctrl &= GB_UART_CTRL_DTR; + } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { + newctrl |= GB_UART_CTRL_DTR; + } + + if (newctrl != gb_tty->ctrlout) { + gb_tty->ctrlout = newctrl; + send_control(gb_tty, newctrl); + } + + if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { + memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); + send_line_coding(gb_tty); + } +} + +static int gb_tty_tiocmget(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | + (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | + TIOCM_CTS; +} + +static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned int newctrl = gb_tty->ctrlout; + + set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + + newctrl = (newctrl & ~clear) | set; + if (gb_tty->ctrlout == newctrl) + return 0; + + gb_tty->ctrlout = newctrl; + return send_control(gb_tty, newctrl); +} + +static void gb_tty_throttle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned char stop_char; + int retval; + + if (I_IXOFF(tty)) { + stop_char = STOP_CHAR(tty); + retval = gb_tty_write(tty, &stop_char, 1); + if (retval <= 0) + return; + } + + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); + } + +} + +static void gb_tty_unthrottle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned char start_char; + int retval; + + if (I_IXOFF(tty)) { + start_char = START_CHAR(tty); + retval = gb_tty_write(tty, &start_char, 1); + if (retval <= 0) + return; + } + + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout |= GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); + } +} + +static int get_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *info) +{ + struct serial_struct tmp; + + if (!info) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; + tmp.type = PORT_16550A; + tmp.line = gb_tty->minor; + tmp.xmit_fifo_size = 16; + tmp.baud_base = 9600; + tmp.close_delay = gb_tty->port.close_delay / 10; + tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; + + if (copy_to_user(info, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + unsigned int closing_wait; + unsigned int close_delay; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + close_delay = new_serial.close_delay * 10; + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + + mutex_lock(&gb_tty->port.mutex); + if (!capable(CAP_SYS_ADMIN)) { + if ((close_delay != gb_tty->port.close_delay) || + (closing_wait != gb_tty->port.closing_wait)) + retval = -EPERM; + else + retval = -EOPNOTSUPP; + } else { + gb_tty->port.close_delay = close_delay; + gb_tty->port.closing_wait = closing_wait; + } + mutex_unlock(&gb_tty->port.mutex); + return retval; +} + +static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) +{ + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old; + struct async_icount new; + + if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) + return -EINVAL; + + do { + spin_lock_irq(&gb_tty->read_lock); + old = gb_tty->oldcount; + new = gb_tty->iocount; + gb_tty->oldcount = new; + spin_unlock_irq(&gb_tty->read_lock); + + if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) + break; + if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) + break; + if ((arg & TIOCM_RI) && (old.rng != new.rng)) + break; + + add_wait_queue(&gb_tty->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&gb_tty->wioctl, &wait); + if (gb_tty->disconnected) { + if (arg & TIOCM_CD) + break; + retval = -ENODEV; + } else if (signal_pending(current)) { + retval = -ERESTARTSYS; + } + } while (!retval); + + return retval; +} + +static int get_serial_usage(struct gb_tty *gb_tty, + struct serial_icounter_struct __user *count) +{ + struct serial_icounter_struct icount; + int retval = 0; + + memset(&icount, 0, sizeof(icount)); + icount.dsr = gb_tty->iocount.dsr; + icount.rng = gb_tty->iocount.rng; + icount.dcd = gb_tty->iocount.dcd; + icount.frame = gb_tty->iocount.frame; + icount.overrun = gb_tty->iocount.overrun; + icount.parity = gb_tty->iocount.parity; + icount.brk = gb_tty->iocount.brk; + + if (copy_to_user(count, &icount, sizeof(icount)) > 0) + retval = -EFAULT; + + return retval; +} + +static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + struct gb_tty *gb_tty = tty->driver_data; + + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(gb_tty, + (struct serial_struct __user *)arg); + case TIOCSSERIAL: + return set_serial_info(gb_tty, + (struct serial_struct __user *)arg); + case TIOCMIWAIT: + return wait_serial_change(gb_tty, arg); + case TIOCGICOUNT: + return get_serial_usage(gb_tty, + (struct serial_icounter_struct __user *)arg); + } + + return -ENOIOCTLCMD; +} + + +static const struct tty_operations gb_ops = { + .install = gb_tty_install, + .open = gb_tty_open, + .close = gb_tty_close, + .cleanup = gb_tty_cleanup, + .hangup = gb_tty_hangup, + .write = gb_tty_write, + .write_room = gb_tty_write_room, + .ioctl = gb_tty_ioctl, + .throttle = gb_tty_throttle, + .unthrottle = gb_tty_unthrottle, + .chars_in_buffer = gb_tty_chars_in_buffer, + .break_ctl = gb_tty_break_ctl, + .set_termios = gb_tty_set_termios, + .tiocmget = gb_tty_tiocmget, + .tiocmset = gb_tty_tiocmset, +}; + + +static int gb_tty_init(void); +static void gb_tty_exit(void); + +static int gb_uart_connection_init(struct gb_connection *connection) +{ + struct gb_tty *gb_tty; + struct device *tty_dev; + int retval; + int minor; + + /* First time here, initialize the tty structures */ + if (atomic_inc_return(&reference_count) == 1) { + retval = gb_tty_init(); + if (retval) { + atomic_dec(&reference_count); + return retval; + } + } + + gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); + if (!gb_tty) + return -ENOMEM; + gb_tty->connection = connection; + connection->private = gb_tty; + + /* Check for compatible protocol version */ + retval = get_version(gb_tty); + if (retval) + goto error_version; + + minor = alloc_minor(gb_tty); + if (minor < 0) { + if (minor == -ENOSPC) { + dev_err(&connection->dev, + "no more free minor numbers\n"); + return -ENODEV; + } + return minor; + } + + gb_tty->minor = minor; + spin_lock_init(&gb_tty->write_lock); + spin_lock_init(&gb_tty->read_lock); + init_waitqueue_head(&gb_tty->wioctl); + mutex_init(&gb_tty->mutex); + + send_control(gb_tty, gb_tty->ctrlout); + + /* initialize the uart to be 9600n81 */ + gb_tty->line_coding.rate = cpu_to_le32(9600); + gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; + gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; + gb_tty->line_coding.data = 8; + send_line_coding(gb_tty); + + tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, + &connection->dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + goto error; + } + + return 0; +error: + release_minor(gb_tty); +error_version: + connection->private = NULL; + kfree(gb_tty); + return retval; +} + +static void gb_uart_connection_exit(struct gb_connection *connection) +{ + struct gb_tty *gb_tty = connection->private; + struct tty_struct *tty; + + if (!gb_tty) + return; + + mutex_lock(&gb_tty->mutex); + gb_tty->disconnected = true; + + wake_up_all(&gb_tty->wioctl); + connection->private = NULL; + mutex_unlock(&gb_tty->mutex); + + tty = tty_port_tty_get(&gb_tty->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + /* FIXME - stop all traffic */ + + tty_unregister_device(gb_tty_driver, gb_tty->minor); + + /* FIXME - free transmit / receive buffers */ + + tty_port_put(&gb_tty->port); + + kfree(gb_tty); + + /* If last device is gone, tear down the tty structures */ + if (atomic_dec_return(&reference_count) == 0) + gb_tty_exit(); +} + +static int gb_tty_init(void) +{ + int retval = 0; + + gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); + if (IS_ERR(gb_tty_driver)) { + pr_err("Can not allocate tty driver\n"); + retval = -ENOMEM; + goto fail_unregister_dev; + } + + gb_tty_driver->driver_name = "gb"; + gb_tty_driver->name = GB_NAME; + gb_tty_driver->major = 0; + gb_tty_driver->minor_start = 0; + gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gb_tty_driver->init_termios = tty_std_termios; + gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(gb_tty_driver, &gb_ops); + + retval = tty_register_driver(gb_tty_driver); + if (retval) { + pr_err("Can not register tty driver: %d\n", retval); + goto fail_put_gb_tty; + } + + return 0; + +fail_put_gb_tty: + put_tty_driver(gb_tty_driver); +fail_unregister_dev: + return retval; +} + +static void gb_tty_exit(void) +{ + int major = MAJOR(gb_tty_driver->major); + int minor = gb_tty_driver->minor_start; + + tty_unregister_driver(gb_tty_driver); + put_tty_driver(gb_tty_driver); + unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); +} + +static struct gb_protocol uart_protocol = { + .name = "uart", + .id = GREYBUS_PROTOCOL_UART, + .major = 0, + .minor = 1, + .connection_init = gb_uart_connection_init, + .connection_exit = gb_uart_connection_exit, + .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ +}; + +int gb_uart_protocol_init(void) +{ + return gb_protocol_register(&uart_protocol); +} + +void gb_uart_protocol_exit(void) +{ + gb_protocol_deregister(&uart_protocol); +} diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c deleted file mode 100644 index 010ef9ee831f..000000000000 --- a/drivers/staging/greybus/usb-gb.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * USB host driver for the Greybus "generic" USB module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - * - */ -#include -#include -#include -#include -#include - -#include "greybus.h" - -/* Version of the Greybus USB protocol we support */ -#define GB_USB_VERSION_MAJOR 0x00 -#define GB_USB_VERSION_MINOR 0x01 - -/* Greybus USB request types */ -#define GB_USB_TYPE_INVALID 0x00 -#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 -#define GB_USB_TYPE_HCD_STOP 0x02 -#define GB_USB_TYPE_HCD_START 0x03 -#define GB_USB_TYPE_URB_ENQUEUE 0x04 -#define GB_USB_TYPE_URB_DEQUEUE 0x05 -#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 -#define GB_USB_TYPE_HUB_CONTROL 0x07 -#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 -#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 - -struct gb_usb_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_usb_urb_enqueue_request { - __le32 pipe; - __le32 transfer_flags; - __le32 transfer_buffer_length; - __le32 maxpacket; - __le32 interval; - __le64 hcpriv_ep; - __le32 number_of_packets; - u8 setup_packet[8]; - u8 payload[0]; -}; - -struct gb_usb_urb_dequeue_request { - __le64 hcpriv_ep; -}; - -struct gb_usb_endpoint_disable_request { - __le64 hcpriv; -}; - -struct gb_usb_hub_control_request { - __le16 typeReq; - __le16 wValue; - __le16 wIndex; - __le16 wLength; -}; - -struct gb_usb_hub_control_response { - u8 buf[0]; -}; - -struct gb_usb_header { - __le16 size; - __le16 id; - __u8 type; -}; - -struct gb_usb_hub_status { - __le32 status; - __le16 buf_size; - u8 buf[0]; -}; - -static struct gb_usb_hub_status *hub_status; // FIXME!!! -static DEFINE_SPINLOCK(hub_status_lock); -static atomic_t frame_number; // FIXME!!! - -struct gb_usb_device { - struct gb_connection *connection; - - struct usb_hcd *hcd; - u8 version_major; - u8 version_minor; -}; - -#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) - -static int get_version(struct gb_usb_device *dev) -{ - struct gb_usb_proto_version_response response; - int ret; - - ret = gb_operation_sync(dev->connection, - GB_USB_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_USB_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_USB_VERSION_MAJOR); - return -ENOTSUPP; - } - dev->version_major = response.major; - dev->version_minor = response.minor; - return 0; -} - -static void hcd_stop(struct usb_hcd *hcd) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, - NULL, 0, NULL, 0); - if (ret) - dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); -} - -static int hcd_start(struct usb_hcd *hcd) -{ - struct usb_bus *bus = hcd_to_bus(hcd); - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, - NULL, 0, NULL, 0); - if (ret) { - dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); - return ret; - } - - hcd->state = HC_STATE_RUNNING; - if (bus->root_hub) - usb_hcd_resume_root_hub(hcd); - return 0; -} - -static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_enqueue_request *request; - struct gb_operation *operation; - int ret; - - operation = gb_operation_create(dev->connection, - GB_USB_TYPE_URB_ENQUEUE, - sizeof(*request) + - urb->transfer_buffer_length, 0); - if (!operation) - return -ENODEV; - - request = operation->request->payload; - request->pipe = cpu_to_le32(urb->pipe); - request->transfer_flags = cpu_to_le32(urb->transfer_flags); - request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); - request->interval = cpu_to_le32(urb->interval); - request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); - request->number_of_packets = cpu_to_le32(urb->number_of_packets); - - memcpy(request->setup_packet, urb->setup_packet, 8); - memcpy(&request->payload, urb->transfer_buffer, - urb->transfer_buffer_length); - - ret = gb_operation_request_send_sync(operation); - gb_operation_destroy(operation); - - return ret; -} - -static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_dequeue_request request; - int ret; - - urb->ep->hcpriv = NULL; - request.hcpriv_ep = cpu_to_le64(urb->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, - &request, sizeof(request), NULL, 0); - urb->hcpriv = NULL; - return ret; -} - -static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_endpoint_disable_request request; - int ret; - - request.hcpriv = cpu_to_le64(ep->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, - &request, sizeof(request), NULL, 0); - ep->hcpriv = NULL; -} - -static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ -} - -static int get_frame_number(struct usb_hcd *hcd) -{ - return atomic_read(&frame_number); -} - -static int hub_status_data(struct usb_hcd *hcd, char *buf) -{ - int retval; - unsigned long flags; - - spin_lock_irqsave(&hub_status_lock, flags); - memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); - retval = le32_to_cpu(hub_status->status); - spin_unlock_irqrestore(&hub_status_lock, flags); - - return retval; -} - -static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, - char *buf, u16 wLength) -{ - struct gb_usb_hub_control_request request; - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - request.typeReq = cpu_to_le16(typeReq); - request.wValue = cpu_to_le16(wValue); - request.wIndex = cpu_to_le16(wIndex); - request.wLength = cpu_to_le16(wLength); - - // FIXME - buf needs to come back in struct gb_usb_hub_control_response - // for some types of requests, depending on typeReq. Do we do this in a - // "generic" way, or only ask for a response for the ones we "know" need - // a response (a small subset of all valid typeReq, thankfully.) - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, - &request, sizeof(request), NULL, 0); - - return ret; -} - -static struct hc_driver usb_gb_hc_driver = { - .description = "greybus_usb", - .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ - .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ - .hcd_priv_size = sizeof(struct gb_usb_device), - - .start = hcd_start, - .stop = hcd_stop, - .urb_enqueue = urb_enqueue, - .urb_dequeue = urb_dequeue, - .endpoint_disable = endpoint_disable, - .endpoint_reset = endpoint_reset, - .get_frame_number = get_frame_number, - .hub_status_data = hub_status_data, - .hub_control = hub_control, -}; - -#if 0 -static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) -{ - __le32 frame_num; - const size_t packet_size = sizeof(struct gb_usb_header) + - sizeof(frame_num); - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - if (le16_to_cpu(hdr->size) != packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - frame_num = (__le32) ((char*) gbuf->transfer_buffer + - sizeof(struct gb_usb_header)); - atomic_set(&frame_number, le32_to_cpu(frame_num)); -} - -static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) -{ - struct gb_usb_hub_status *new_hubstatus, *hubstatus; - struct gb_usb_header* hdr = gbuf->transfer_buffer; - const size_t min_packet_size = sizeof(struct gb_usb_header) + - sizeof(struct gb_usb_hub_status); - unsigned long flags; - - if (le16_to_cpu(hdr->size) < min_packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer - + sizeof(struct gb_usb_header)); - - if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { - pr_err("%s(): invalid packet size, dropping packet\n", - __func__); - return; - } - - new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); - memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); - - spin_lock_irqsave(&hub_status_lock, flags); - hubstatus = hub_status; - hub_status = new_hubstatus; - spin_unlock_irqrestore(&hub_status_lock, flags); - - kfree(hubstatus); -} - -static void gb_usb_in_handler(struct gbuf *gbuf) -{ - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - switch (hdr->type) { - case GB_USB_TYPE_GET_FRAME_NUMBER: - gb_usb_handle_get_frame_number(gbuf); - break; - - case GB_USB_TYPE_HUB_STATUS_DATA: - gb_usb_handle_hubs_status_data(gbuf); - break; - } -} -#endif - -static int gb_usb_connection_init(struct gb_connection *connection) -{ - struct device *dev = &connection->dev; - struct gb_usb_device *gb_usb_dev; - - int retval; - - gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); - if (!gb_usb_dev) - return -ENOMEM; - - gb_usb_dev->connection = connection; - connection->private = gb_usb_dev; - - /* Check for compatible protocol version */ - retval = get_version(gb_usb_dev); - if (retval) - goto error_create_hcd; - - gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); - if (!gb_usb_dev->hcd) { - retval = -ENODEV; - goto error_create_hcd; - } - - gb_usb_dev->hcd->has_tt = 1; - gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; - - retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); - if (retval) - goto error_add_hcd; - - return 0; -error_add_hcd: - usb_put_hcd(gb_usb_dev->hcd); -error_create_hcd: - kfree(gb_usb_dev); - return retval; -} - -static void gb_usb_connection_exit(struct gb_connection *connection) -{ - // FIXME - tear everything down! -} - -static struct gb_protocol usb_protocol = { - .name = "usb", - .id = GREYBUS_PROTOCOL_USB, - .major = 0, - .minor = 1, - .connection_init = gb_usb_connection_init, - .connection_exit = gb_usb_connection_exit, - .request_recv = NULL, /* FIXME we have requests!!! */ -}; - -int gb_usb_protocol_init(void) -{ - return gb_protocol_register(&usb_protocol); -} - -void gb_usb_protocol_exit(void) -{ - gb_protocol_deregister(&usb_protocol); -} diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c new file mode 100644 index 000000000000..010ef9ee831f --- /dev/null +++ b/drivers/staging/greybus/usb.c @@ -0,0 +1,396 @@ +/* + * USB host driver for the Greybus "generic" USB module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + * + */ +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Version of the Greybus USB protocol we support */ +#define GB_USB_VERSION_MAJOR 0x00 +#define GB_USB_VERSION_MINOR 0x01 + +/* Greybus USB request types */ +#define GB_USB_TYPE_INVALID 0x00 +#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 +#define GB_USB_TYPE_HCD_STOP 0x02 +#define GB_USB_TYPE_HCD_START 0x03 +#define GB_USB_TYPE_URB_ENQUEUE 0x04 +#define GB_USB_TYPE_URB_DEQUEUE 0x05 +#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 +#define GB_USB_TYPE_HUB_CONTROL 0x07 +#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 +#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 + +struct gb_usb_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_usb_urb_enqueue_request { + __le32 pipe; + __le32 transfer_flags; + __le32 transfer_buffer_length; + __le32 maxpacket; + __le32 interval; + __le64 hcpriv_ep; + __le32 number_of_packets; + u8 setup_packet[8]; + u8 payload[0]; +}; + +struct gb_usb_urb_dequeue_request { + __le64 hcpriv_ep; +}; + +struct gb_usb_endpoint_disable_request { + __le64 hcpriv; +}; + +struct gb_usb_hub_control_request { + __le16 typeReq; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +}; + +struct gb_usb_hub_control_response { + u8 buf[0]; +}; + +struct gb_usb_header { + __le16 size; + __le16 id; + __u8 type; +}; + +struct gb_usb_hub_status { + __le32 status; + __le16 buf_size; + u8 buf[0]; +}; + +static struct gb_usb_hub_status *hub_status; // FIXME!!! +static DEFINE_SPINLOCK(hub_status_lock); +static atomic_t frame_number; // FIXME!!! + +struct gb_usb_device { + struct gb_connection *connection; + + struct usb_hcd *hcd; + u8 version_major; + u8 version_minor; +}; + +#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) + +static int get_version(struct gb_usb_device *dev) +{ + struct gb_usb_proto_version_response response; + int ret; + + ret = gb_operation_sync(dev->connection, + GB_USB_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_USB_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_USB_VERSION_MAJOR); + return -ENOTSUPP; + } + dev->version_major = response.major; + dev->version_minor = response.minor; + return 0; +} + +static void hcd_stop(struct usb_hcd *hcd) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, + NULL, 0, NULL, 0); + if (ret) + dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); +} + +static int hcd_start(struct usb_hcd *hcd) +{ + struct usb_bus *bus = hcd_to_bus(hcd); + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, + NULL, 0, NULL, 0); + if (ret) { + dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); + return ret; + } + + hcd->state = HC_STATE_RUNNING; + if (bus->root_hub) + usb_hcd_resume_root_hub(hcd); + return 0; +} + +static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_enqueue_request *request; + struct gb_operation *operation; + int ret; + + operation = gb_operation_create(dev->connection, + GB_USB_TYPE_URB_ENQUEUE, + sizeof(*request) + + urb->transfer_buffer_length, 0); + if (!operation) + return -ENODEV; + + request = operation->request->payload; + request->pipe = cpu_to_le32(urb->pipe); + request->transfer_flags = cpu_to_le32(urb->transfer_flags); + request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); + request->interval = cpu_to_le32(urb->interval); + request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); + request->number_of_packets = cpu_to_le32(urb->number_of_packets); + + memcpy(request->setup_packet, urb->setup_packet, 8); + memcpy(&request->payload, urb->transfer_buffer, + urb->transfer_buffer_length); + + ret = gb_operation_request_send_sync(operation); + gb_operation_destroy(operation); + + return ret; +} + +static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_dequeue_request request; + int ret; + + urb->ep->hcpriv = NULL; + request.hcpriv_ep = cpu_to_le64(urb->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, + &request, sizeof(request), NULL, 0); + urb->hcpriv = NULL; + return ret; +} + +static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_endpoint_disable_request request; + int ret; + + request.hcpriv = cpu_to_le64(ep->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, + &request, sizeof(request), NULL, 0); + ep->hcpriv = NULL; +} + +static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ +} + +static int get_frame_number(struct usb_hcd *hcd) +{ + return atomic_read(&frame_number); +} + +static int hub_status_data(struct usb_hcd *hcd, char *buf) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&hub_status_lock, flags); + memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); + retval = le32_to_cpu(hub_status->status); + spin_unlock_irqrestore(&hub_status_lock, flags); + + return retval; +} + +static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength) +{ + struct gb_usb_hub_control_request request; + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + request.typeReq = cpu_to_le16(typeReq); + request.wValue = cpu_to_le16(wValue); + request.wIndex = cpu_to_le16(wIndex); + request.wLength = cpu_to_le16(wLength); + + // FIXME - buf needs to come back in struct gb_usb_hub_control_response + // for some types of requests, depending on typeReq. Do we do this in a + // "generic" way, or only ask for a response for the ones we "know" need + // a response (a small subset of all valid typeReq, thankfully.) + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, + &request, sizeof(request), NULL, 0); + + return ret; +} + +static struct hc_driver usb_gb_hc_driver = { + .description = "greybus_usb", + .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ + .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ + .hcd_priv_size = sizeof(struct gb_usb_device), + + .start = hcd_start, + .stop = hcd_stop, + .urb_enqueue = urb_enqueue, + .urb_dequeue = urb_dequeue, + .endpoint_disable = endpoint_disable, + .endpoint_reset = endpoint_reset, + .get_frame_number = get_frame_number, + .hub_status_data = hub_status_data, + .hub_control = hub_control, +}; + +#if 0 +static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) +{ + __le32 frame_num; + const size_t packet_size = sizeof(struct gb_usb_header) + + sizeof(frame_num); + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + if (le16_to_cpu(hdr->size) != packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + frame_num = (__le32) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + atomic_set(&frame_number, le32_to_cpu(frame_num)); +} + +static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) +{ + struct gb_usb_hub_status *new_hubstatus, *hubstatus; + struct gb_usb_header* hdr = gbuf->transfer_buffer; + const size_t min_packet_size = sizeof(struct gb_usb_header) + + sizeof(struct gb_usb_hub_status); + unsigned long flags; + + if (le16_to_cpu(hdr->size) < min_packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + + if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { + pr_err("%s(): invalid packet size, dropping packet\n", + __func__); + return; + } + + new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); + memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); + + spin_lock_irqsave(&hub_status_lock, flags); + hubstatus = hub_status; + hub_status = new_hubstatus; + spin_unlock_irqrestore(&hub_status_lock, flags); + + kfree(hubstatus); +} + +static void gb_usb_in_handler(struct gbuf *gbuf) +{ + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + switch (hdr->type) { + case GB_USB_TYPE_GET_FRAME_NUMBER: + gb_usb_handle_get_frame_number(gbuf); + break; + + case GB_USB_TYPE_HUB_STATUS_DATA: + gb_usb_handle_hubs_status_data(gbuf); + break; + } +} +#endif + +static int gb_usb_connection_init(struct gb_connection *connection) +{ + struct device *dev = &connection->dev; + struct gb_usb_device *gb_usb_dev; + + int retval; + + gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); + if (!gb_usb_dev) + return -ENOMEM; + + gb_usb_dev->connection = connection; + connection->private = gb_usb_dev; + + /* Check for compatible protocol version */ + retval = get_version(gb_usb_dev); + if (retval) + goto error_create_hcd; + + gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); + if (!gb_usb_dev->hcd) { + retval = -ENODEV; + goto error_create_hcd; + } + + gb_usb_dev->hcd->has_tt = 1; + gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; + + retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); + if (retval) + goto error_add_hcd; + + return 0; +error_add_hcd: + usb_put_hcd(gb_usb_dev->hcd); +error_create_hcd: + kfree(gb_usb_dev); + return retval; +} + +static void gb_usb_connection_exit(struct gb_connection *connection) +{ + // FIXME - tear everything down! +} + +static struct gb_protocol usb_protocol = { + .name = "usb", + .id = GREYBUS_PROTOCOL_USB, + .major = 0, + .minor = 1, + .connection_init = gb_usb_connection_init, + .connection_exit = gb_usb_connection_exit, + .request_recv = NULL, /* FIXME we have requests!!! */ +}; + +int gb_usb_protocol_init(void) +{ + return gb_protocol_register(&usb_protocol); +} + +void gb_usb_protocol_exit(void) +{ + gb_protocol_deregister(&usb_protocol); +}