[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