/* * QEMU e1000 emulation * * Software developer's manual: * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf * * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. * Copyright (c) 2008 Qumranet * Based on work done by: * Copyright (c) 2007 Dan Aloni * Copyright (c) 2004 Antony T Curtis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" #include "net/checksum.h" #include "hw/loader.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" #include "qemu/iov.h" #include "qemu/range.h" #include "e1000_regs.h" static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; #define E1000_DEBUG #ifdef E1000_DEBUG enum { DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, DEBUG_RXFILTER, DEBUG_PHY, DEBUG_NOTYET, }; #define DBGBIT(x) (1<= 10.6 * Others never tested */ typedef struct E1000State_st { /*< private >*/ PCIDevice parent_obj; /*< public >*/ NICState *nic; NICConf conf; MemoryRegion mmio; MemoryRegion io; uint32_t mac_reg[0x8000]; uint16_t phy_reg[0x20]; uint16_t eeprom_data[64]; uint32_t rxbuf_size; uint32_t rxbuf_min_shift; struct e1000_tx { unsigned char header[256]; unsigned char vlan_header[4]; /* Fields vlan and data must not be reordered or separated. */ unsigned char vlan[4]; unsigned char data[0x10000]; uint16_t size; unsigned char sum_needed; unsigned char vlan_needed; uint8_t ipcss; uint8_t ipcso; uint16_t ipcse; uint8_t tucss; uint8_t tucso; uint16_t tucse; uint8_t hdr_len; uint16_t mss; uint32_t paylen; uint16_t tso_frames; char tse; int8_t ip; int8_t tcp; char cptse; // current packet tse bit } tx; struct { uint32_t val_in; /* shifted in from guest driver */ uint16_t bitnum_in; uint16_t bitnum_out; uint16_t reading; uint32_t old_eecd; } eecd_state; QEMUTimer *autoneg_timer; QEMUTimer *mit_timer; /* Mitigation timer. */ bool mit_timer_on; /* Mitigation timer is running. */ bool mit_irq_level; /* Tracks interrupt pin level. */ uint32_t mit_ide; /* Tracks E1000_TXD_CMD_IDE bit. */ /* Compatibility flags for migration to/from qemu 1.3.0 and older */ #define E1000_FLAG_AUTONEG_BIT 0 #define E1000_FLAG_MIT_BIT 1 #define E1000_FLAG_MAC_BIT 2 #define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) #define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT) #define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT) uint32_t compat_flags; } E1000State; #define chkflag(x) (s->compat_flags & E1000_FLAG_##x) typedef struct E1000BaseClass { PCIDeviceClass parent_class; uint16_t phy_id2; } E1000BaseClass; #define TYPE_E1000_BASE "e1000-base" #define E1000(obj) \ OBJECT_CHECK(E1000State, (obj), TYPE_E1000_BASE) #define E1000_DEVICE_CLASS(klass) \ OBJECT_CLASS_CHECK(E1000BaseClass, (klass), TYPE_E1000_BASE) #define E1000_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) #define defreg(x) x = (E1000_##x>>2) enum { defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA), defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT), defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH), defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT), defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT), defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC), defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR), defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC), defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC), defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), defreg(MPTC), defreg(BPTC) }; static void e1000_link_down(E1000State *s) { s->mac_reg[STATUS] &= ~E1000_STATUS_LU; s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS; s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; s->phy_reg[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; } static void e1000_link_up(E1000State *s) { s->mac_reg[STATUS] |= E1000_STATUS_LU; s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS; /* E1000_STATUS_LU is tested by e1000_can_receive() */ qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static bool have_autoneg(E1000State *s) { return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); } static void set_phy_ctrl(E1000State *s, int index, uint16_t val) { /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */ s->phy_reg[PHY_CTRL] = val & ~(0x3f | MII_CR_RESET | MII_CR_RESTART_AUTO_NEG); /* * QEMU 1.3 does not support link auto-negotiation emulation, so if we * migrate during auto negotiation, after migration the link will be * down. */ if (have_autoneg(s) && (val & MII_CR_RESTART_AUTO_NEG)) { e1000_link_down(s); DBGOUT(PHY, "Start link auto negotiation\n"); timer_mod(s->autoneg_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); } } static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = { [PHY_CTRL] = set_phy_ctrl, }; enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) }; enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W }; static const char phy_regcap[0x20] = { [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, [PHY_AUTONEG_EXP] = PHY_R, }; /* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ static const uint16_t phy_reg_init[] = { [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | MII_CR_FULL_DUPLEX | MII_CR_AUTO_NEG_EN, [PHY_STATUS] = MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | /* link initially up */ MII_SR_AUTONEG_CAPS | /* MII_SR_AUTONEG_COMPLETE: initially NOT completed */ MII_SR_PREAMBLE_SUPPRESS | MII_SR_EXTENDED_STATUS | MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | MII_SR_100X_FD_CAPS, [PHY_ID1] = 0x141, /* [PHY_ID2] configured per DevId, from e1000_reset() */ [PHY_AUTONEG_ADV] = 0xde1, [PHY_LP_ABILITY] = 0x1e0, [PHY_1000T_CTRL] = 0x0e00, [PHY_1000T_STATUS] = 0x3c00, [M88E1000_PHY_SPEC_CTRL] = 0x360, [M88E1000_PHY_SPEC_STATUS] = 0xac00, [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60, }; static const uint32_t mac_reg_init[] = { [PBA] = 0x00100030, [LEDCTL] = 0x602, [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | E1000_CTRL_SPD_1000 | E1000_CTRL_SLU, [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK | E1000_STATUS_SPEED_1000 | E1000_STATUS_FD | E1000_STATUS_LU, [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | E1000_MANC_ARP_EN | E1000_MANC_0298_EN | E1000_MANC_RMCP_EN, }; /* Helper function, *curr == 0 means the value is not set */ static inline void mit_update_delay(uint32_t *curr, uint32_t value) { if (value && (*curr == 0 || value < *curr)) { *curr = value; } } static void set_interrupt_cause(E1000State *s, int index, uint32_t val) { PCIDevice *d = PCI_DEVICE(s); uint32_t pending_ints; uint32_t mit_delay; s->mac_reg[ICR] = val; /* * Make sure ICR and ICS registers have the same value. * The spec says that the ICS register is write-only. However in practice, * on real hardware ICS is readable, and for reads it has the same value as * ICR (except that ICS does not have the clear on read behaviour of ICR). * * The VxWorks PRO/1000 driver uses this behaviour. */ s->mac_reg[ICS] = val; pending_ints = (s->mac_reg[IMS] & s->mac_reg[ICR]); if (!s->mit_irq_level && pending_ints) { /* * Here we detect a potential raising edge. We postpone raising the * interrupt line if we are inside the mitigation delay window * (s->mit_timer_on == 1). * We provide a partial implementation of interrupt mitigation, * emulating only RADV, TADV and ITR (lower 16 bits, 1024ns units for * RADV and TADV, 256ns units for ITR). RDTR is only used to enable * RADV; relative timers based on TIDV and RDTR are not implemented. */ if (s->mit_timer_on) { return; } if (chkflag(MIT)) { /* Compute the next mitigation delay according to pending * interrupts and the current values of RADV (provided * RDTR!=0), TADV and ITR. * Then rearm the timer. */ mit_delay = 0; if (s->mit_ide && (pending_ints & (E1000_ICR_TXQE | E1000_ICR_TXDW))) { mit_update_delay(&mit_delay, s->mac_reg[TADV] * 4); } if (s->mac_reg[RDTR] && (pending_ints & E1000_ICS_RXT0)) { mit_update_delay(&mit_delay, s->mac_reg[RADV] * 4); } mit_update_delay(&mit_delay, s->mac_reg[ITR]); /* * According to e1000 SPEC, the Ethernet controller guarantees * a maximum observable interrupt rate of 7813 interrupts/sec. * Thus if mit_delay < 500 then the delay should be set to the * minimum delay possible which is 500. */ mit_delay = (mit_delay < 500) ? 500 : mit_delay; if (mit_delay) { s->mit_timer_on = 1; timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + mit_delay * 256); } s->mit_ide = 0; } } s->mit_irq_level = (pending_ints != 0); pci_set_irq(d, s->mit_irq_level); } static void e1000_mit_timer(void *opaque) { E1000State *s = opaque; s->mit_timer_on = 0; /* Call set_interrupt_cause to update the irq level (if necessary). */ set_interrupt_cause(s, 0, s->mac_reg[ICR]); } static void set_ics(E1000State *s, int index, uint32_t val) { DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR], s->mac_reg[IMS]); set_interrupt_cause(s, 0, val | s->mac_reg[ICR]); } static void e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; if (!qemu_get_queue(s->nic)->link_down) { e1000_link_up(s); s->phy_reg[PHY_LP_ABILITY] |= MII_LPAR_LPACK; s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Auto negotiation is completed\n"); set_ics(s, 0, E1000_ICS_LSC); /* signal link status change to guest */ } } static int rxbufsize(uint32_t v) { v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 | E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 | E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256; switch (v) { case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384: return 16384; case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192: return 8192; case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096: return 4096; case E1000_RCTL_SZ_1024: return 1024; case E1000_RCTL_SZ_512: return 512; case E1000_RCTL_SZ_256: return 256; } return 2048; } static void e1000_reset(void *opaque) { E1000State *d = opaque; E1000BaseClass *edc = E1000_DEVICE_GET_CLASS(d); uint8_t *macaddr = d->conf.macaddr.a; int i; timer_
/*
 * drivers/mfd/mfd-core.c
 *
 * core MFD support
 * Copyright (c) 2006 Ian Molton
 * Copyright (c) 2007,2008 Dmitry Baryshkov
 *
 * 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.
 *
 */

#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>

static struct device_type mfd_dev_type = {
	.name	= "mfd_device",
};

int mfd_cell_enable(struct platform_device *pdev)
{
	const struct mfd_cell *cell = mfd_get_cell(pdev);
	int err = 0;

	/* only call enable hook if the cell wasn't previously enabled */
	if (atomic_inc_return(cell->usage_count) == 1)
		err = cell->enable(pdev);

	/* if the enable hook failed, decrement counter to allow retries */
	if (err)
		atomic_dec(cell->usage_count);

	return err;
}
EXPORT_SYMBOL(mfd_cell_enable);

int mfd_cell_disable(struct platform_device *pdev)
{
	const struct mfd_cell *cell = mfd_get_cell(pdev);
	int err = 0;

	/* only disable if no other clients are using it */
	if (atomic_dec_return(cell->usage_count) == 0)
		err = cell->disable(pdev);

	/* if the disable hook failed, increment to allow retries */
	if (err)
		atomic_inc(cell->usage_count);

	/* sanity check; did someone call disable too many times? */
	WARN_ON(atomic_read(cell->usage_count) < 0);

	return err;
}
EXPORT_SYMBOL(mfd_cell_disable);

static int mfd_platform_add_cell(struct platform_device *pdev,
				 const struct mfd_cell *cell,
				 atomic_t *usage_count)
{
	if (!cell)
		return 0;

	pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
	if (!pdev->mfd_cell)
		return -ENOMEM;

	pdev->mfd_cell->usage_count = usage_count;
	return 0;
}

#if IS_ENABLED(CONFIG_ACPI)
static void mfd_acpi_add_device(const struct mfd_cell *cell,
				struct platform_device *pdev)
{
	struct acpi_device *parent_adev;
	struct acpi_device *adev;

	parent_adev = ACPI_COMPANION(pdev->dev.parent);
	if (!parent_adev)
		return;

	/*
	 * MFD child device gets its ACPI handle either from the ACPI
	 * device directly under the parent that matches the acpi_pnpid or
	 * it will use the parent handle if is no acpi_pnpid is given.
	 */
	adev = parent_adev;
	if (cell->acpi_pnpid) {
		struct acpi_device_id ids[2] = {};
		struct acpi_device *child_adev;

		strlcpy(ids[0].id, cell->acpi_pnpid, sizeof(ids[0].id));
		list_for_each_entry(child_adev, &parent_adev->children, node)
			if (acpi_match_device_ids(child_adev, ids)) {
				adev = child_adev;
				break;
			}
	}

	ACPI_COMPANION_SET(&pdev->dev, adev);
}
#else
static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
				       struct platform_device *pdev)
{
}
#endif

static int mfd_add_device(struct device *parent, int id,
			  const struct mfd_cell *cell, atomic_t *usage_count,
			  struct resource *mem_base,
			  int irq_base, struct irq_domain *domain)
{
	struct resource *res;
	struct platform_device *pdev;
	struct device_node *np = NULL;
	int ret = -ENOMEM;
	int platform_id;
	int r;

	if (id == PLATFORM_DEVID_AUTO)
		platform_id = id;
	else
		platform_id = id + cell->id;

	pdev = platform_device_alloc(cell->name, platform_id);
	if (!pdev)
		goto fail_alloc;

	res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
	if (!res)
		goto fail_device;

	pdev->dev.parent = parent;
	pdev->dev.type = &mfd_dev_type;
	pdev->dev.dma_mask = parent->dma_mask;
	pdev->dev.dma_parms = parent->dma_parms;
	pdev->dev.coherent_dma_mask = parent->coherent_dma_mask;

	ret = regulator_bulk_register_supply_alias(
			&pdev->dev, cell->parent_supplies,
			parent, cell->parent_supplies,
			cell->num_parent_supplies);
	if (ret < 0)
		goto fail_res;

	if (parent->of_node && cell->of_compatible) {
		for_each_child_of_node(parent->of_node, np) {
			if (of_device_is_compatible(np, cell->of_compatible)) {
				pdev->dev.of_node = np;
				break;
			}
		}
	}

	mfd_acpi_add_device(cell, pdev);

	if (cell->pdata_size) {
		ret = platform_device_add_data(pdev,
					cell->platform_data, cell->pdata_size);
		if (ret)
			goto fail_alias;
	}

	ret = mfd_platform_add_cell(pdev, cell, usage_count);
	if (ret)
		goto fail_alias;

	for (r = 0; r < cell->num_resources; r++) {
		res[r].name = cell->resources[r].name;
		res[r].flags = cell->resources[r].flags;

		/* Find out base to use */
		if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
			res[r].parent = mem_base;
			res[r].start = mem_base->start +
				cell->resources[r].start;
			res[r].end = mem_base->start +
				cell->resources[r].end;
		} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
			if (domain) {
				/* Unable to create mappings for IRQ ranges. */
				WARN_ON(cell->resources[r].start !=
					cell->resources[r].end);
				res[r].start = res[r].end = irq_create_mapping(
					domain, cell->resources[r].start);
			} else {
				res[r].start = irq_base +
					cell->resources[r].start;
				res[r].end   = irq_base +
					cell->resources[r].end;
			}
		} else {
			res[r].parent = cell->resources[r].parent;
			res[r].start = cell->resources[r].start;
			res[r].end   = cell->resources[r].end;
		}

		if (!cell->ignore_resource_conflicts) {
			ret = acpi_check_resource_conflict(&res[r]);
			if (ret)
				goto fail_alias;
		}
	}

	ret = platform_device_add_resources(pdev, res, cell->num_resources);
	if (ret)
		goto fail_alias;

	ret = platform_device_add(pdev);
	if (ret)
		goto fail_alias;

	if (cell->pm_runtime_no_callbacks)
		pm_runtime_no_callbacks(&pdev->dev);

	kfree(res);

	return 0;

fail_alias:
	regulator_bulk_unregister_supply_alias(&pdev->dev,
					       cell->parent_supplies,
					       cell->num_parent_supplies);
fail_res:
	kfree(res);
fail_device:
	platform_device_put(pdev);
fail_alloc:
	return ret;
}

int mfd_add_devices(struct device *parent, int id,
		    const struct mfd_cell *cells, int n_devs,
		    struct resource *mem_base,
		    int irq_base, struct irq_domain *domain)
{
	int i;
	int ret;
	atomic_t *cnts;

	/* initialize reference counting for all cells */
	cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL);
	if (!cnts)
		return -ENOMEM;

	for (i = 0; i < n_devs; i++) {
		atomic_set(&cnts[i], 0);
		ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,
				     irq_base, domain);
		if (ret)
			goto fail;
	}

	return 0;

fail:
	if (i)
		mfd_remove_devices(parent);
	else
		kfree(cnts);
	return ret;
}
EXPORT_SYMBOL(mfd_add_devices);

static int mfd_remove_devices_fn(struct device *dev, void *c)
{
	struct platform_device *pdev;
	const struct mfd_cell *cell;
	atomic_t **usage_count = c;

	if (dev->type != &mfd_dev_type)
		return 0;

	pdev = to_platform_device(dev);
	cell = mfd_get_cell(pdev);

	regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
					       cell->num_parent_supplies);

	/* find the base address of usage_count pointers (for freeing) */
	if (!*usage_count || (cell->usage_count < *usage_count))
		*usage_count = cell->usage_count;

	platform_device_unregister(pdev);
	return 0;
}

void mfd_remove_devices(struct device *parent)
{
	atomic_t *cnts = NULL;

	device_for_each_child(parent, &cnts, mfd_remove_devices_fn);
	kfree(cnts);
}
EXPORT_SYMBOL(mfd_remove_devices);

int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
{
	struct mfd_cell cell_entry;
	struct device *dev;
	struct platform_device *pdev;
	int i;

	/* fetch the parent cell's device (should already be registered!) */
	dev = bus_find_device_by_name(&platform_bus_type, NULL, cell);
	if (!dev) {
		printk(KERN_ERR "failed to find device for cell %s\n", cell);
		return -ENODEV;
	}
	pdev = to_platform_device(dev);
	memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry));

	WARN_ON(!cell_entry.enable);

	for (i = 0; i < n_clones; i++) {
		cell_entry.name = clones[i