summaryrefslogtreecommitdiffstats
path: root/arch/sandbox/board/hostfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sandbox/board/hostfile.c')
-rw-r--r--arch/sandbox/board/hostfile.c246
1 files changed, 194 insertions, 52 deletions
diff --git a/arch/sandbox/board/hostfile.c b/arch/sandbox/board/hostfile.c
index e5a7580d07..7afad95b6d 100644
--- a/arch/sandbox/board/hostfile.c
+++ b/arch/sandbox/board/hostfile.c
@@ -3,9 +3,6 @@
*
* Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
@@ -19,25 +16,29 @@
#include <common.h>
#include <driver.h>
+#include <block.h>
+#include <disks.h>
#include <malloc.h>
#include <mach/linux.h>
#include <init.h>
#include <errno.h>
#include <linux/err.h>
#include <mach/hostfile.h>
+#include <featctrl.h>
#include <xfuncs.h>
-#include <linux/err.h>
-
struct hf_priv {
- struct cdev cdev;
+ union {
+ struct block_device blk;
+ struct cdev cdev;
+ };
const char *filename;
int fd;
+ struct feature_controller feat;
};
-static ssize_t hf_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
+static ssize_t hf_read(struct hf_priv *priv, void *buf, size_t count, loff_t offset, ulong flags)
{
- struct hf_priv *priv= cdev->priv;
int fd = priv->fd;
if (linux_lseek(fd, offset) != offset)
@@ -46,9 +47,8 @@ static ssize_t hf_read(struct cdev *cdev, void *buf, size_t count, loff_t offset
return linux_read(fd, buf, count);
}
-static ssize_t hf_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, ulong flags)
+static ssize_t hf_write(struct hf_priv *priv, const void *buf, size_t count, loff_t offset, ulong flags)
{
- struct hf_priv *priv = cdev->priv;
int fd = priv->fd;
if (linux_lseek(fd, offset) != offset)
@@ -57,59 +57,135 @@ static ssize_t hf_write(struct cdev *cdev, const void *buf, size_t count, loff_t
return linux_write(fd, buf, count);
}
-static void hf_info(struct device_d *dev)
+static ssize_t hf_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
+{
+ return hf_read(cdev->priv, buf, count, offset, flags);
+}
+
+static ssize_t hf_cdev_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, ulong flags)
+{
+ return hf_write(cdev->priv, buf, count, offset, flags);
+}
+
+static struct cdev_operations hf_cdev_ops = {
+ .read = hf_cdev_read,
+ .write = hf_cdev_write,
+};
+
+static int hf_blk_read(struct block_device *blk, void *buf, sector_t block, blkcnt_t num_blocks)
+{
+ ssize_t ret = hf_read(container_of(blk, struct hf_priv, blk), buf,
+ num_blocks << SECTOR_SHIFT, block << SECTOR_SHIFT, 0);
+ return ret > 0 ? 0 : ret;
+}
+
+static int hf_blk_write(struct block_device *blk, const void *buf, sector_t block, blkcnt_t num_blocks)
+{
+ ssize_t ret = hf_write(container_of(blk, struct hf_priv, blk), buf,
+ num_blocks << SECTOR_SHIFT, block << SECTOR_SHIFT, 0);
+ return ret > 0 ? 0 : ret;
+}
+
+static struct block_device_ops hf_blk_ops = {
+ .read = hf_blk_read,
+ .write = hf_blk_write,
+};
+
+static void hf_info(struct device *dev)
{
struct hf_priv *priv = dev->priv;
printf("file: %s\n", priv->filename);
}
-static struct cdev_operations hf_fops = {
- .read = hf_read,
- .write = hf_write,
-};
+static int hostfile_feat_check(struct feature_controller *feat, int idx)
+{
+ struct hf_priv *priv = container_of(feat, struct hf_priv, feat);
-static int hf_probe(struct device_d *dev)
+ return priv->fd >= 0 ? FEATCTRL_OKAY : FEATCTRL_GATED;
+}
+
+static int hf_probe(struct device *dev)
{
+ struct device_node *np = dev->of_node;
struct hf_priv *priv = xzalloc(sizeof(*priv));
- struct resource *res;
+ struct cdev *cdev;
+ bool is_featctrl = false, is_blockdev;
+ u64 reg[2];
int err;
- res = dev_get_resource(dev, IORESOURCE_MEM, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
+ if (!np)
+ return -ENODEV;
+
+ dev->priv = priv;
+ priv->fd = -1;
- priv->cdev.size = resource_size(res);
+ if (IS_ENABLED(CONFIG_FEATURE_CONTROLLER) &&
+ of_property_read_bool(np, "barebox,feature-controller")) {
+ priv->feat.dev = dev;
+ priv->feat.check = hostfile_feat_check;
- if (!dev->device_node)
- return -ENODEV;
+ err = feature_controller_register(&priv->feat);
+ if (err)
+ return err;
- of_property_read_u32(dev->device_node, "barebox,fd", &priv->fd);
+ is_featctrl = true;
+ }
- err = of_property_read_string(dev->device_node, "barebox,filename",
- &priv->filename);
+
+ err = of_property_read_u64_array(np, "reg", reg, ARRAY_SIZE(reg));
if (err)
return err;
- if (!priv->fd)
- priv->fd = linux_open(priv->filename, true);
+ of_property_read_u32(np, "barebox,fd", &priv->fd);
+
+ err = of_property_read_string(np, "barebox,filename",
+ &priv->filename);
+ if (err)
+ return err;
- priv->cdev.name = dev->device_node->name;
- priv->cdev.dev = dev;
- priv->cdev.ops = &hf_fops;
- priv->cdev.priv = priv;
+ if (priv->fd < 0)
+ return is_featctrl ? 0 : priv->fd;
dev->info = hf_info;
- dev->priv = priv;
- err = devfs_create(&priv->cdev);
- if (err)
- return err;
+ is_blockdev = of_property_read_bool(np, "barebox,blockdev");
- of_parse_partitions(&priv->cdev, dev->device_node);
- of_partitions_register_fixup(&priv->cdev);
+ cdev = is_blockdev ? &priv->blk.cdev : &priv->cdev;
- return 0;
+ cdev_set_of_node(cdev, np);
+
+ if (is_blockdev) {
+ cdev->name = np->name;
+ priv->blk.dev = dev;
+ priv->blk.ops = &hf_blk_ops;
+ priv->blk.blockbits = SECTOR_SHIFT;
+ priv->blk.num_blocks = reg[1] / SECTOR_SIZE;
+ priv->blk.type = BLK_TYPE_VIRTUAL;
+
+ err = blockdevice_register(&priv->blk);
+ if (err)
+ return err;
+
+ dev_info(dev, "registered as block device\n");
+ } else {
+ cdev->name = np->name;
+ cdev->dev = dev;
+ cdev->ops = &hf_cdev_ops;
+ cdev->size = reg[1];
+ cdev->priv = priv;
+
+ err = devfs_create(cdev);
+ if (err)
+ return err;
+
+ dev_info(dev, "registered as character device\n");
+ }
+
+ of_parse_partitions(cdev, np);
+ of_partitions_register_fixup(cdev);
+
+ return of_platform_populate(np, NULL, dev);
}
static __maybe_unused struct of_device_id hostfile_dt_ids[] = {
@@ -119,45 +195,111 @@ static __maybe_unused struct of_device_id hostfile_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, hostfile_dt_ids);
-static struct driver_d hf_drv = {
+static struct driver hf_drv = {
.name = "hostfile",
.of_compatible = DRV_OF_COMPAT(hostfile_dt_ids),
.probe = hf_probe,
};
-coredevice_platform_driver(hf_drv);
+device_platform_driver(hf_drv);
static int of_hostfile_fixup(struct device_node *root, void *ctx)
{
struct hf_info *hf = ctx;
struct device_node *node;
- uint32_t reg[] = {
- hf->base >> 32,
- hf->base,
- hf->size
- };
+ bool name_only = false;
int ret;
- node = of_new_node(root, hf->devname);
+ node = of_get_child_by_name(root, hf->devname);
+ if (node)
+ name_only = true;
+ else
+ node = of_new_node(root, hf->devname);
- ret = of_property_write_string(node, "compatible", hostfile_dt_ids->compatible);
+ ret = of_property_write_string(node, "barebox,filename", hf->filename);
if (ret)
return ret;
- ret = of_property_write_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
+ if (name_only)
+ return 0;
+
+ ret = of_property_write_string(node, "compatible", hostfile_dt_ids->compatible);
if (ret)
return ret;
- ret = of_property_write_u32(node, "barebox,fd", hf->fd);
+ ret = of_property_write_bool(node, "barebox,blockdev", hf->is_blockdev);
if (ret)
return ret;
- ret = of_property_write_string(node, "barebox,filename", hf->filename);
+ ret = of_property_write_bool(node, "barebox,cdev", hf->is_cdev);
+ if (ret)
+ return ret;
- return ret;
+ return of_property_write_bool(node, "barebox,read-only", hf->is_readonly);
}
int barebox_register_filedev(struct hf_info *hf)
{
return of_register_fixup(of_hostfile_fixup, hf);
}
+
+static int of_hostfile_map_fixup(struct device_node *root, void *ctx)
+{
+ struct device_node *node;
+ int ret;
+
+ for_each_compatible_node_from(node, root, NULL, "barebox,stickypage") {
+ char *filename;
+
+ filename = linux_get_stickypage_path();
+ if (!filename) {
+ pr_err("error allocating stickypage\n");
+ continue;
+ }
+
+ of_property_write_string(node, "barebox,filename", filename);
+ of_property_write_string(node, "compatible", "barebox,hostfile");
+ }
+
+ for_each_compatible_node_from(node, root, NULL, "barebox,hostfile") {
+ struct hf_info hf = {};
+ uint64_t reg[2] = {};
+
+ hf.devname = node->name;
+
+ ret = of_property_read_string(node, "barebox,filename", &hf.filename);
+ if (ret) {
+ pr_err("skipping nameless hostfile %s\n", hf.devname);
+ continue;
+ }
+
+ hf.is_blockdev = of_property_read_bool(node, "barebox,blockdev");
+ hf.is_cdev = of_property_read_bool(node, "barebox,cdev");
+ hf.is_readonly = of_property_read_bool(node, "barebox,read-only");
+
+ of_property_read_u64_array(node, "reg", reg, ARRAY_SIZE(reg));
+
+ hf.base = reg[0];
+ hf.size = reg[1];
+
+ ret = linux_open_hostfile(&hf);
+ if (ret)
+ continue;
+
+ reg[0] = hf.base;
+ reg[1] = hf.size;
+
+ of_property_write_u64_array(node, "reg", reg, ARRAY_SIZE(reg));
+ of_property_write_bool(node, "barebox,blockdev", hf.is_blockdev);
+ of_property_write_u32(node, "barebox,fd", hf.fd);
+ }
+
+ return 0;
+}
+
+static int barebox_fixup_filedevs(void)
+{
+ return of_register_fixup(of_hostfile_map_fixup, NULL);
+}
+pure_initcall(barebox_fixup_filedevs);