summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile1
-rw-r--r--common/blspec.c31
-rw-r--r--common/bootsource.c1
-rw-r--r--common/console.c24
-rw-r--r--common/reset_source.c23
-rw-r--r--common/serdev.c208
6 files changed, 284 insertions, 4 deletions
diff --git a/common/Makefile b/common/Makefile
index 4e9681f20..1ff7d2370 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 6171461a7..2c682e199 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 707b07924..78ecd8267 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 f4c799fa5..ab3d4d397 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 06e2ca85f..338d7b9ac 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 000000000..4a6dbefe6
--- /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;
+}