diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/blspec.c | 31 | ||||
-rw-r--r-- | common/bootsource.c | 1 | ||||
-rw-r--r-- | common/console.c | 24 | ||||
-rw-r--r-- | common/reset_source.c | 23 | ||||
-rw-r--r-- | common/serdev.c | 208 |
6 files changed, 284 insertions, 4 deletions
diff --git a/common/Makefile b/common/Makefile index 4e9681f203..1ff7d2370b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_FIRMWARE) += firmware.o obj-$(CONFIG_UBIFORMAT) += ubiformat.o obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o obj-$(CONFIG_BOOT) += boot.o +obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o ifdef CONFIG_PASSWORD diff --git a/common/blspec.c b/common/blspec.c index 6171461a72..2c682e1990 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -420,6 +420,30 @@ out: } /* + * entry_is_match_machine_id - check if a bootspec entry is match with + * the machine id given by global variable. + * + * returns true if the entry is match, false otherwise + */ + +static bool entry_is_match_machine_id(struct blspec_entry *entry) +{ + int ret = true; + const char *env_machineid = getenv_nonempty("global.boot.machine_id"); + + if (env_machineid) { + const char *machineid = blspec_entry_var_get(entry, "machine-id"); + if (!machineid || strcmp(machineid, env_machineid)) { + pr_debug("ignoring entry with missmatched machine-id " \ + "\"%s\" != \"%s\"\n", env_machineid, machineid); + ret = false; + } + } + + return ret; +} + +/* * blspec_scan_directory - scan over a directory * * Given a root path collects all bootentries entries found under /bootentries/entries/. @@ -504,6 +528,11 @@ int blspec_scan_directory(struct bootentries *bootentries, const char *root) continue; } + if (!entry_is_match_machine_id(entry)) { + blspec_entry_free(&entry->entry); + continue; + } + found++; if (entry->cdev && entry->cdev->dev) { @@ -756,4 +785,4 @@ static int blspec_init(void) { return bootentry_register_provider(blspec_bootentry_provider); } -device_initcall(blspec_init);
\ No newline at end of file +device_initcall(blspec_init); diff --git a/common/bootsource.c b/common/bootsource.c index 707b07924c..78ecd82676 100644 --- a/common/bootsource.c +++ b/common/bootsource.c @@ -36,6 +36,7 @@ static const char *bootsource_str[] = { [BOOTSOURCE_HD] = "harddisk", [BOOTSOURCE_USB] = "usb", [BOOTSOURCE_NET] = "net", + [BOOTSOURCE_CAN] = "can", }; static enum bootsource bootsource = BOOTSOURCE_UNKNOWN; diff --git a/common/console.c b/common/console.c index f4c799fa54..ab3d4d397c 100644 --- a/common/console.c +++ b/common/console.c @@ -305,10 +305,11 @@ static ssize_t fops_write(struct cdev* dev, const void* buf, size_t count, int console_register(struct console_device *newcdev) { + struct device_node *serdev_node = console_is_serdev_node(newcdev); struct device_d *dev = &newcdev->class_dev; int activate = 0, ret; - if (initialized == CONSOLE_UNINITIALIZED) + if (!serdev_node && initialized == CONSOLE_UNINITIALIZED) console_init_early(); if (newcdev->devname) { @@ -323,6 +324,17 @@ int console_register(struct console_device *newcdev) dev->parent = newcdev->dev; platform_device_register(dev); + newcdev->open_count = 0; + + /* + * If our console device is a serdev, we skip the creation of + * corresponding entry in /dev as well as registration in + * console_list and just go straight to populating child + * devices. + */ + if (serdev_node) + return of_platform_populate(serdev_node, NULL, dev); + if (newcdev->setbrg) { ret = newcdev->setbrg(newcdev, CONFIG_BAUDRATE); if (ret) @@ -335,8 +347,6 @@ int console_register(struct console_device *newcdev) if (newcdev->putc && !newcdev->puts) newcdev->puts = __console_puts; - newcdev->open_count = 0; - dev_add_param_string(dev, "active", console_active_set, console_active_get, &newcdev->active_string, newcdev); @@ -386,6 +396,14 @@ int console_unregister(struct console_device *cdev) struct device_d *dev = &cdev->class_dev; int status; + /* + * We don't do any sophisticated serdev device de-population + * and instead claim this console busy, preventing its + * de-initialization, 'till the very end of our execution. + */ + if (console_is_serdev_node(cdev)) + return -EBUSY; + devfs_remove(&cdev->devfs); list_del(&cdev->list); diff --git a/common/reset_source.c b/common/reset_source.c index 06e2ca85f5..338d7b9acb 100644 --- a/common/reset_source.c +++ b/common/reset_source.c @@ -32,6 +32,7 @@ static const char * const reset_src_names[] = { static enum reset_src_type reset_source; static unsigned int reset_source_priority; +static int reset_source_instance; enum reset_src_type reset_source_get(void) { @@ -39,6 +40,12 @@ enum reset_src_type reset_source_get(void) } EXPORT_SYMBOL(reset_source_get); +int reset_source_get_instance(void) +{ + return reset_source_instance; +} +EXPORT_SYMBOL(reset_source_get_instance); + void reset_source_set_priority(enum reset_src_type st, unsigned int priority) { if (priority <= reset_source_priority) @@ -46,17 +53,33 @@ void reset_source_set_priority(enum reset_src_type st, unsigned int priority) reset_source = st; reset_source_priority = priority; + reset_source_instance = 0; pr_debug("Setting reset source to %s with priority %d\n", reset_src_names[reset_source], priority); } EXPORT_SYMBOL(reset_source_set_priority); +const char *reset_source_name(void) +{ + return reset_src_names[reset_source]; +} +EXPORT_SYMBOL(reset_source_name); + +void reset_source_set_instance(enum reset_src_type type, int instance) +{ + if (reset_source == type) + reset_source_instance = instance; +} +EXPORT_SYMBOL(reset_source_set_instance); + static int reset_source_init(void) { globalvar_add_simple_enum("system.reset", (unsigned int *)&reset_source, reset_src_names, ARRAY_SIZE(reset_src_names)); + globalvar_add_simple_int("system.reset_instance", &reset_source_instance, + "%d"); return 0; } diff --git a/common/serdev.c b/common/serdev.c new file mode 100644 index 0000000000..4a6dbefe61 --- /dev/null +++ b/common/serdev.c @@ -0,0 +1,208 @@ + +#include <common.h> +#include <serdev.h> + +static void serdev_device_poller(void *context) +{ + struct serdev_device *serdev = context; + struct console_device *cdev = to_console_device(serdev); + unsigned char *buf = serdev->buf; + int ret, len; + + /* + * Since this callback is a part of poller infrastructure we + * want to use _non_interruptible version of the function + * below to prevent recursion from happening (regular + * console_drain will call is_timeout, which might end up + * calling this function again). + */ + len = console_drain_non_interruptible(cdev, serdev->fifo, buf, + PAGE_SIZE, + serdev->polling_window); + while (len > 0) { + ret = serdev->receive_buf(serdev, buf, len); + len -= ret; + buf += ret; + } + + if (serdev->polling_interval) { + /* + * Re-schedule ourselves in 'serdev->polling_interval' + * nanoseconds + */ + poller_call_async(&serdev->poller, + serdev->polling_interval, + serdev_device_poller, + serdev); + } else { + poller_async_cancel(&serdev->poller); + } +} + +static int serdev_device_set_polling_interval(struct param_d *param, void *serdev) +{ + /* + * We execute poller ever time polling_interval changes to get + * any unprocessed immediate Rx data as well as to propagate + * polling_interval chagnes to outstanging async pollers. + */ + serdev_device_poller(serdev); + return 0; +} + +int serdev_device_open(struct serdev_device *serdev) +{ + struct console_device *cdev = to_console_device(serdev); + struct param_d *p; + int ret; + + if (!cdev->putc || !cdev->getc) + return -EINVAL; + + if (!serdev->polling_window) + return -EINVAL; + + serdev->buf = xzalloc(PAGE_SIZE); + serdev->fifo = kfifo_alloc(PAGE_SIZE); + if (!serdev->fifo) + return -ENOMEM; + + ret = poller_async_register(&serdev->poller); + if (ret) + return ret; + + ret = console_open(cdev); + if (ret) + return ret; + + p = dev_add_param_uint64(serdev->dev, "polling_interval", + serdev_device_set_polling_interval, NULL, + &serdev->polling_interval, "%llu", serdev); + if (IS_ERR(p)) + return PTR_ERR(p); + + return 0; +} + +unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, + unsigned int speed) +{ + struct console_device *cdev = to_console_device(serdev); + + if (console_set_baudrate(cdev, speed) < 0) + return 0; + + return console_get_baudrate(cdev); +} + +int serdev_device_write(struct serdev_device *serdev, const unsigned char *buf, + size_t count, unsigned long timeout) +{ + struct console_device *cdev = to_console_device(serdev); + + while (count--) + cdev->putc(cdev, *buf++); + /* + * Poll Rx once right after we just send some data in case our + * serdev device implements command/response type of a + * protocol and we need to start draining input as soon as + * possible. + */ + serdev_device_poller(serdev); + return 0; +} + +/* + * NOTE: Code below is given primarily as an example of serdev API + * usage. It may or may not be as useful or work as well as the + * functions above. + */ + +struct serdev_device_reader { + unsigned char *buf; + size_t len; + size_t capacity; +}; + +static int serdev_device_reader_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, + size_t size) +{ + struct device_d *dev = serdev->dev; + struct serdev_device_reader *r = dev->priv; + const size_t room = min(r->capacity - r->len, size); + + memcpy(r->buf + r->len, buf, room); + r->len += room; + /* + * It's important we return 'size' even if we didn't trully + * consume all of the data, since otherwise serdev will keep + * re-executing us until we do. Given the logic above that + * would mean infinite loop. + */ + return size; +} + +/** + * serdev_device_reader_open - Open a reader serdev device + * + * @serdev: Underlying serdev device + * @capacity: Storage capacity of the reader + * + * This function is inteded for creating of reader serdev devices that + * can be used in conjunction with serdev_device_read() to perform + * trivial fixed length reads from a serdev device. + */ +int serdev_device_reader_open(struct serdev_device *serdev, size_t capacity) +{ + struct serdev_device_reader *r; + + if (serdev->receive_buf) + return -EINVAL; + + r = xzalloc(sizeof(*r)); + r->capacity = capacity; + r->buf = xzalloc(capacity); + + serdev->receive_buf = serdev_device_reader_receive_buf; + serdev->dev->priv = r; + + return 0; +} + +/** + * serdev_device_read - Read data from serdev device + * + * @serdev: Serdev device to read from (must be a serdev reader) + * @buf: Buffer to read data into + * @count: Number of bytes to read + * @timeout: Read operation timeout + * + */ +int serdev_device_read(struct serdev_device *serdev, unsigned char *buf, + size_t count, unsigned long timeout) +{ + struct device_d *dev = serdev->dev; + struct serdev_device_reader *r = dev->priv; + int ret; + + uint64_t start = get_time_ns(); + + if (serdev->receive_buf != serdev_device_reader_receive_buf) + return -EINVAL; + + /* + * is_timeout will implicitly poll serdev via poller + * infrastructure + */ + while (r->len < count) { + if (is_timeout(start, timeout)) + return -ETIMEDOUT; + } + + memcpy(buf, r->buf, count); + ret = (r->len == count) ? 0 : -EMSGSIZE; + r->len = 0; + + return ret; +} |