[U-Boot] [PATCH 03/11] DM: add block controller core

Pavel Herrmann morpheus.ibis at gmail.com
Thu Sep 20 21:37:39 CEST 2012


This core provides unified access to different block controllers (SATA, SCSI).

Signed-off-by: Pavel Herrmann <morpheus.ibis at gmail.com>
---
 Makefile                   |   1 +
 drivers/blockctrl/Makefile |  42 ++++++
 drivers/blockctrl/core.c   | 349 +++++++++++++++++++++++++++++++++++++++++++++
 include/dm/blockctrl.h     |  75 ++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 drivers/blockctrl/Makefile
 create mode 100644 drivers/blockctrl/core.c
 create mode 100644 include/dm/blockctrl.h

diff --git a/Makefile b/Makefile
index e43fd9d..4420484 100644
--- a/Makefile
+++ b/Makefile
@@ -304,6 +304,7 @@ LIBS-y += test/libtest.o
 LIBS-$(CONFIG_DM) += common/dm/libdm.o
 LIBS-$(CONFIG_DM) += drivers/demo/libdemo.o
 LIBS-${CONFIG_DM_BLOCK} += drivers/blockdev/libblockdev.o
+LIBS-${CONFIG_DM_BLOCK} += drivers/blockctrl/libblockctrl.o
 
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
diff --git a/drivers/blockctrl/Makefile b/drivers/blockctrl/Makefile
new file mode 100644
index 0000000..21a9094
--- /dev/null
+++ b/drivers/blockctrl/Makefile
@@ -0,0 +1,42 @@
+# 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 as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	:= $(obj)libblockctrl.o
+
+COBJS-${CONFIG_DM_BLOCK} := core.o
+
+COBJS	:= $(COBJS-y)
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/blockctrl/core.c b/drivers/blockctrl/core.c
new file mode 100644
index 0000000..1f1f834
--- /dev/null
+++ b/drivers/blockctrl/core.c
@@ -0,0 +1,349 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at gmail.com>
+ *
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <dm/blockctrl.h>
+#include <dm/manager.h>
+#include <malloc.h>
+
+struct blockctrl_core_entry {
+	struct list_head	list;
+	struct instance		*instance;
+	struct blockctrl_ops	*ops;
+};
+
+static struct blockctrl_core_entry *get_entry_by_instance(struct instance *dev)
+{
+	struct blockctrl_core_entry *entry;
+	struct core_instance *core = get_core_instance(CORE_BLOCKCTRL);
+
+	if (!core)
+		return NULL;
+
+	list_for_each_entry(entry, &core->succ, list) {
+		if (entry->instance == dev)
+			return entry;
+	}
+
+	return NULL;
+}
+
+/* Core API functions */
+static int get_count(struct core_instance *core)
+{
+	int count = 0;
+	struct blockctrl_core_entry *entry;
+
+	list_for_each_entry(entry, &core->succ, list) {
+		count++;
+	}
+
+	return count;
+}
+
+static struct instance *get_child(struct core_instance *core, int index)
+{
+	struct blockctrl_core_entry *entry = NULL;
+
+	list_for_each_entry(entry, &core->succ, list) {
+		if (!index)
+			return entry->instance;
+		index--;
+	}
+
+	return NULL;
+}
+
+static int bind(struct core_instance *core, struct instance *dev, void *ops,
+	void *data)
+{
+	struct blockctrl_core_entry *entry;
+
+	if (ops == NULL)
+		return -EINVAL;
+
+	entry = malloc(sizeof(*entry));
+	if (entry == NULL)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&entry->list);
+	entry->instance = dev;
+	entry->ops = ops;
+	list_add_tail(&entry->list, &core->succ);
+
+	return 0;
+}
+
+static int unbind(struct core_instance *core, struct instance *dev)
+{
+	struct blockctrl_core_entry *entry, *n;
+
+	list_for_each_entry_safe(entry, n, &core->succ, list) {
+		if (entry->instance == dev) {
+			list_del(&entry->list);
+			free(entry);
+		}
+	}
+
+	return 0;
+}
+
+static int replace(struct core_instance *core, struct instance *new,
+	struct instance *old)
+{
+	struct blockctrl_core_entry *entry = get_entry_by_instance(old);
+
+	if (!entry)
+		return -ENOENT;
+
+	entry->instance = new;
+
+	return 0;
+}
+
+static int init(struct core_instance *core)
+{
+	INIT_LIST_HEAD(&core->succ);
+	core->private_data = NULL;
+
+	return 0;
+}
+
+static int reloc(struct core_instance *core, struct core_instance *old)
+{
+	struct blockctrl_core_entry *entry, *new;
+
+	/* no private_data to copy, yet */
+
+	/* fixup links in old list and prepare new list head */
+	/* FIXME */
+	/* list_fix_reloc(&old->succ); */
+	INIT_LIST_HEAD(&core->succ);
+	core->private_data = NULL;
+
+	/* copy list entries to new memory */
+	list_for_each_entry(entry, &old->succ, list) {
+		new = malloc(sizeof(*new));
+		if (!new)
+			return -ENOMEM;
+
+		INIT_LIST_HEAD(&new->list);
+		new->instance = entry->instance;
+		new->ops = entry->ops;
+		list_add_tail(&new->list, &core->succ);
+		/*no free at this point, old memory should not be freed*/
+	}
+
+	return 0;
+}
+
+static int destroy(struct core_instance *core)
+{
+	struct blockctrl_core_entry *entry, *n;
+
+	/* destroy private data */
+	free(core->private_data);
+	core->private_data = NULL;
+
+	/* destroy successor list */
+	list_for_each_entry_safe(entry, n, &core->succ, list) {
+		list_del(&entry->list);
+		free(entry);
+	}
+
+	return 0;
+}
+
+U_BOOT_CORE(CORE_BLOCKCTRL,
+	init,
+	reloc,
+	destroy,
+	get_count,
+	get_child,
+	bind,
+	unbind,
+	replace);
+
+lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start,
+	lbaint_t length, void *buffer)
+{
+	struct blockctrl_core_entry *entry;
+	struct blockctrl_ops *driver_ops;
+	int error;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return -ENOENT;
+
+	error = driver_activate(i);
+	if (error)
+		return error;
+
+	driver_ops = entry->ops;
+	if (!driver_ops || !driver_ops->read)
+		return -EINVAL;
+
+	return driver_ops->read(i, port, start, length, buffer);
+}
+
+lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start,
+	lbaint_t length, void *buffer)
+{
+	struct blockctrl_core_entry *entry;
+	struct blockctrl_ops *driver_ops;
+	int error;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return -ENOENT;
+
+	error = driver_activate(i);
+	if (error)
+		return error;
+
+	driver_ops = entry->ops;
+	if (!driver_ops || !driver_ops->write)
+		return -EINVAL;
+
+	return driver_ops->write(i, port, start, length, buffer);
+}
+
+int blockctrl_scan(struct instance *i, int port)
+{
+	struct blockctrl_core_entry *entry;
+	struct blockctrl_ops *driver_ops;
+	int error;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return -ENOENT;
+
+	error = driver_activate(i);
+	if (error)
+		return error;
+
+	driver_ops = entry->ops;
+	if (!driver_ops || !driver_ops->scan)
+		return -EINVAL;
+
+	return driver_ops->scan(i, port);
+}
+
+int blockctrl_get_port_count(struct instance *i)
+{
+	struct blockctrl_core_entry *entry;
+	struct blockctrl_ops *driver_ops;
+	int error;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return -ENOENT;
+
+	error = driver_activate(i);
+	if (error)
+		return error;
+
+	driver_ops = entry->ops;
+	if (!driver_ops || !driver_ops->get_port_count)
+		return -EINVAL;
+
+	return driver_ops->get_port_count(i);
+}
+
+int blockctrl_get_port_option(struct instance *i, int port,
+	enum blockctrl_port_option_code op, struct option *result)
+{
+	struct blockctrl_core_entry *entry;
+	struct blockctrl_ops *driver_ops;
+	int error;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return -ENOENT;
+
+	error = driver_activate(i);
+	if (error)
+		return error;
+
+	driver_ops = entry->ops;
+	if (!driver_ops || !driver_ops->get_port_option)
+		return -EINVAL;
+
+	return driver_ops->get_port_option(i, port, op, result);
+}
+
+struct instance *blockctrl_rescan_port(struct instance *i, int port)
+{
+	/* we assume that all children of blockctrl are blockdev_ata */
+	struct instance *child = NULL;
+	struct blockctrl_core_entry *entry;
+	struct driver_instance *di;
+	struct blockdev_ata_platform_data *pdata;
+	struct driver_info *info;
+	int nfound;
+
+	entry = get_entry_by_instance(i);
+	if (!entry)
+		return NULL;
+
+	list_for_each_entry(di, &i->succ, list) {
+		if (di->i.info)
+			pdata = di->i.info->platform_data;
+		else
+			pdata = NULL;
+
+		if (pdata && (pdata->port_number == port))
+			child = &di->i;
+	}
+
+	/*
+	 * If we have an active link, we check whether this is a new device,
+	 * in which case we simply bind a new instance, or an old device,
+	 * in which case we reactivate the device to force partition rescan.
+	 * If we dont have an active link, we remove any device attached.
+	 */
+	nfound = blockctrl_scan(i, port);
+	if (!nfound) {
+		if (child) {
+			/* rescan the disk size and partitions, just in case */
+			driver_remove(child);
+			scan_partitions(child);
+		} else {
+			pdata = malloc(sizeof(*pdata));
+			pdata->port_number = port;
+			info = malloc(sizeof(*info));
+			info->platform_data = pdata;
+			info->name = "blockdev_ata";
+			child = driver_bind(i, info);
+			scan_partitions(child);
+			return child;
+		}
+	} else {
+		/* link is not active, remove and unbind the child device */
+		if (child) {
+			driver_remove(child);
+			driver_unbind(child);
+		}
+	}
+
+	return NULL;
+}
diff --git a/include/dm/blockctrl.h b/include/dm/blockctrl.h
new file mode 100644
index 0000000..4b6d582
--- /dev/null
+++ b/include/dm/blockctrl.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at gmail.com>
+ *
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_BLOCKCTRL_H_
+#define _DM_BLOCKCTRL_H 1
+
+#include <dm/structures.h>
+#include <dm/blockdev.h>
+
+enum blockctrl_port_option_code {
+	BLKP_OPT_IFTYPE = 0,
+	BLKP_OPT_IFSPEED,
+	BLKP_OPT_BLOCKSIZE,
+	BLKP_OPT_BLOCKCOUNT,
+	BLKP_OPT_REMOVABLE,
+	BLKP_OPT_LBA48,
+	BLKP_OPT_VENDOR,
+	BLKP_OPT_PRODUCT,
+	BLKP_OPT_REVISION,
+};
+
+enum blockctrl_iftype {
+	BLKP_IFTYPE_SATA,
+	BLKP_IFTYPE_PATA,
+	BLKP_IFTYPE_SCSI,
+	BLKP_IFTYPE_VIRTUAL,
+};
+
+
+struct blockctrl_ops {
+	lbaint_t	(*read)(struct instance *i, int port, lbaint_t start,
+				lbaint_t length, void *buffer);
+	lbaint_t	(*write)(struct instance *i, int port, lbaint_t start,
+				lbaint_t length, void *buffer);
+	int		(*scan)(struct instance *i, int port);
+	int		(*get_port_count)(struct instance *i);
+	int		(*get_port_option)(struct instance *i, int port,
+				enum blockctrl_port_option_code op,
+				struct option *result);
+};
+
+/* driver wrappers */
+lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start,
+	lbaint_t length, void *buffer);
+lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start,
+	lbaint_t length, void *buffer);
+int blockctrl_scan(struct instance *i, int port);
+int blockctrl_get_port_count(struct instance *i);
+int blockctrl_get_port_option(struct instance *i, int port,
+	enum blockctrl_port_option_code op, struct option *result);
+
+/* command helpers */
+struct instance *blockctrl_rescan_port(struct instance *i, int port);
+
+#endif
-- 
1.7.12



More information about the U-Boot mailing list