162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell EBU SoC Device Bus Controller
462306a36Sopenharmony_ci * (memory controller for NOR/NAND/SRAM/FPGA devices)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2013-2014 Marvell
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/clk.h>
1562306a36Sopenharmony_ci#include <linux/mbus.h>
1662306a36Sopenharmony_ci#include <linux/of_platform.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Register definitions */
2162306a36Sopenharmony_ci#define ARMADA_DEV_WIDTH_SHIFT		30
2262306a36Sopenharmony_ci#define ARMADA_BADR_SKEW_SHIFT		28
2362306a36Sopenharmony_ci#define ARMADA_RD_HOLD_SHIFT		23
2462306a36Sopenharmony_ci#define ARMADA_ACC_NEXT_SHIFT		17
2562306a36Sopenharmony_ci#define ARMADA_RD_SETUP_SHIFT		12
2662306a36Sopenharmony_ci#define ARMADA_ACC_FIRST_SHIFT		6
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define ARMADA_SYNC_ENABLE_SHIFT	24
2962306a36Sopenharmony_ci#define ARMADA_WR_HIGH_SHIFT		16
3062306a36Sopenharmony_ci#define ARMADA_WR_LOW_SHIFT		8
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define ARMADA_READ_PARAM_OFFSET	0x0
3362306a36Sopenharmony_ci#define ARMADA_WRITE_PARAM_OFFSET	0x4
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define ORION_RESERVED			(0x2 << 30)
3662306a36Sopenharmony_ci#define ORION_BADR_SKEW_SHIFT		28
3762306a36Sopenharmony_ci#define ORION_WR_HIGH_EXT_BIT		BIT(27)
3862306a36Sopenharmony_ci#define ORION_WR_HIGH_EXT_MASK		0x8
3962306a36Sopenharmony_ci#define ORION_WR_LOW_EXT_BIT		BIT(26)
4062306a36Sopenharmony_ci#define ORION_WR_LOW_EXT_MASK		0x8
4162306a36Sopenharmony_ci#define ORION_ALE_WR_EXT_BIT		BIT(25)
4262306a36Sopenharmony_ci#define ORION_ALE_WR_EXT_MASK		0x8
4362306a36Sopenharmony_ci#define ORION_ACC_NEXT_EXT_BIT		BIT(24)
4462306a36Sopenharmony_ci#define ORION_ACC_NEXT_EXT_MASK		0x10
4562306a36Sopenharmony_ci#define ORION_ACC_FIRST_EXT_BIT		BIT(23)
4662306a36Sopenharmony_ci#define ORION_ACC_FIRST_EXT_MASK	0x10
4762306a36Sopenharmony_ci#define ORION_TURN_OFF_EXT_BIT		BIT(22)
4862306a36Sopenharmony_ci#define ORION_TURN_OFF_EXT_MASK		0x8
4962306a36Sopenharmony_ci#define ORION_DEV_WIDTH_SHIFT		20
5062306a36Sopenharmony_ci#define ORION_WR_HIGH_SHIFT		17
5162306a36Sopenharmony_ci#define ORION_WR_HIGH_MASK		0x7
5262306a36Sopenharmony_ci#define ORION_WR_LOW_SHIFT		14
5362306a36Sopenharmony_ci#define ORION_WR_LOW_MASK		0x7
5462306a36Sopenharmony_ci#define ORION_ALE_WR_SHIFT		11
5562306a36Sopenharmony_ci#define ORION_ALE_WR_MASK		0x7
5662306a36Sopenharmony_ci#define ORION_ACC_NEXT_SHIFT		7
5762306a36Sopenharmony_ci#define ORION_ACC_NEXT_MASK		0xF
5862306a36Sopenharmony_ci#define ORION_ACC_FIRST_SHIFT		3
5962306a36Sopenharmony_ci#define ORION_ACC_FIRST_MASK		0xF
6062306a36Sopenharmony_ci#define ORION_TURN_OFF_SHIFT		0
6162306a36Sopenharmony_ci#define ORION_TURN_OFF_MASK		0x7
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct devbus_read_params {
6462306a36Sopenharmony_ci	u32 bus_width;
6562306a36Sopenharmony_ci	u32 badr_skew;
6662306a36Sopenharmony_ci	u32 turn_off;
6762306a36Sopenharmony_ci	u32 acc_first;
6862306a36Sopenharmony_ci	u32 acc_next;
6962306a36Sopenharmony_ci	u32 rd_setup;
7062306a36Sopenharmony_ci	u32 rd_hold;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct devbus_write_params {
7462306a36Sopenharmony_ci	u32 sync_enable;
7562306a36Sopenharmony_ci	u32 wr_high;
7662306a36Sopenharmony_ci	u32 wr_low;
7762306a36Sopenharmony_ci	u32 ale_wr;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct devbus {
8162306a36Sopenharmony_ci	struct device *dev;
8262306a36Sopenharmony_ci	void __iomem *base;
8362306a36Sopenharmony_ci	unsigned long tick_ps;
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int get_timing_param_ps(struct devbus *devbus,
8762306a36Sopenharmony_ci			       struct device_node *node,
8862306a36Sopenharmony_ci			       const char *name,
8962306a36Sopenharmony_ci			       u32 *ticks)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	u32 time_ps;
9262306a36Sopenharmony_ci	int err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	err = of_property_read_u32(node, name, &time_ps);
9562306a36Sopenharmony_ci	if (err < 0) {
9662306a36Sopenharmony_ci		dev_err(devbus->dev, "%pOF has no '%s' property\n",
9762306a36Sopenharmony_ci			node, name);
9862306a36Sopenharmony_ci		return err;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	*ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n",
10462306a36Sopenharmony_ci		name, time_ps, *ticks);
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int devbus_get_timing_params(struct devbus *devbus,
10962306a36Sopenharmony_ci				    struct device_node *node,
11062306a36Sopenharmony_ci				    struct devbus_read_params *r,
11162306a36Sopenharmony_ci				    struct devbus_write_params *w)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int err;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width);
11662306a36Sopenharmony_ci	if (err < 0) {
11762306a36Sopenharmony_ci		dev_err(devbus->dev,
11862306a36Sopenharmony_ci			"%pOF has no 'devbus,bus-width' property\n",
11962306a36Sopenharmony_ci			node);
12062306a36Sopenharmony_ci		return err;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/*
12462306a36Sopenharmony_ci	 * The bus width is encoded into the register as 0 for 8 bits,
12562306a36Sopenharmony_ci	 * and 1 for 16 bits, so we do the necessary conversion here.
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (r->bus_width == 8) {
12862306a36Sopenharmony_ci		r->bus_width = 0;
12962306a36Sopenharmony_ci	} else if (r->bus_width == 16) {
13062306a36Sopenharmony_ci		r->bus_width = 1;
13162306a36Sopenharmony_ci	} else {
13262306a36Sopenharmony_ci		dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width);
13362306a36Sopenharmony_ci		return -EINVAL;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
13762306a36Sopenharmony_ci				  &r->badr_skew);
13862306a36Sopenharmony_ci	if (err < 0)
13962306a36Sopenharmony_ci		return err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
14262306a36Sopenharmony_ci				  &r->turn_off);
14362306a36Sopenharmony_ci	if (err < 0)
14462306a36Sopenharmony_ci		return err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
14762306a36Sopenharmony_ci				  &r->acc_first);
14862306a36Sopenharmony_ci	if (err < 0)
14962306a36Sopenharmony_ci		return err;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
15262306a36Sopenharmony_ci				  &r->acc_next);
15362306a36Sopenharmony_ci	if (err < 0)
15462306a36Sopenharmony_ci		return err;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (of_device_is_compatible(devbus->dev->of_node, "marvell,mvebu-devbus")) {
15762306a36Sopenharmony_ci		err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps",
15862306a36Sopenharmony_ci					  &r->rd_setup);
15962306a36Sopenharmony_ci		if (err < 0)
16062306a36Sopenharmony_ci			return err;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps",
16362306a36Sopenharmony_ci					  &r->rd_hold);
16462306a36Sopenharmony_ci		if (err < 0)
16562306a36Sopenharmony_ci			return err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		err = of_property_read_u32(node, "devbus,sync-enable",
16862306a36Sopenharmony_ci					   &w->sync_enable);
16962306a36Sopenharmony_ci		if (err < 0) {
17062306a36Sopenharmony_ci			dev_err(devbus->dev,
17162306a36Sopenharmony_ci				"%pOF has no 'devbus,sync-enable' property\n",
17262306a36Sopenharmony_ci				node);
17362306a36Sopenharmony_ci			return err;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
17862306a36Sopenharmony_ci				  &w->ale_wr);
17962306a36Sopenharmony_ci	if (err < 0)
18062306a36Sopenharmony_ci		return err;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
18362306a36Sopenharmony_ci				  &w->wr_low);
18462306a36Sopenharmony_ci	if (err < 0)
18562306a36Sopenharmony_ci		return err;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
18862306a36Sopenharmony_ci				  &w->wr_high);
18962306a36Sopenharmony_ci	if (err < 0)
19062306a36Sopenharmony_ci		return err;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic void devbus_orion_set_timing_params(struct devbus *devbus,
19662306a36Sopenharmony_ci					  struct device_node *node,
19762306a36Sopenharmony_ci					  struct devbus_read_params *r,
19862306a36Sopenharmony_ci					  struct devbus_write_params *w)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u32 value;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/*
20362306a36Sopenharmony_ci	 * The hardware designers found it would be a good idea to
20462306a36Sopenharmony_ci	 * split most of the values in the register into two fields:
20562306a36Sopenharmony_ci	 * one containing all the low-order bits, and another one
20662306a36Sopenharmony_ci	 * containing just the high-order bit. For all of those
20762306a36Sopenharmony_ci	 * fields, we have to split the value into these two parts.
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	value =	(r->turn_off   & ORION_TURN_OFF_MASK)  << ORION_TURN_OFF_SHIFT  |
21062306a36Sopenharmony_ci		(r->acc_first  & ORION_ACC_FIRST_MASK) << ORION_ACC_FIRST_SHIFT |
21162306a36Sopenharmony_ci		(r->acc_next   & ORION_ACC_NEXT_MASK)  << ORION_ACC_NEXT_SHIFT  |
21262306a36Sopenharmony_ci		(w->ale_wr     & ORION_ALE_WR_MASK)    << ORION_ALE_WR_SHIFT    |
21362306a36Sopenharmony_ci		(w->wr_low     & ORION_WR_LOW_MASK)    << ORION_WR_LOW_SHIFT    |
21462306a36Sopenharmony_ci		(w->wr_high    & ORION_WR_HIGH_MASK)   << ORION_WR_HIGH_SHIFT   |
21562306a36Sopenharmony_ci		r->bus_width                           << ORION_DEV_WIDTH_SHIFT |
21662306a36Sopenharmony_ci		((r->turn_off  & ORION_TURN_OFF_EXT_MASK)  ? ORION_TURN_OFF_EXT_BIT  : 0) |
21762306a36Sopenharmony_ci		((r->acc_first & ORION_ACC_FIRST_EXT_MASK) ? ORION_ACC_FIRST_EXT_BIT : 0) |
21862306a36Sopenharmony_ci		((r->acc_next  & ORION_ACC_NEXT_EXT_MASK)  ? ORION_ACC_NEXT_EXT_BIT  : 0) |
21962306a36Sopenharmony_ci		((w->ale_wr    & ORION_ALE_WR_EXT_MASK)    ? ORION_ALE_WR_EXT_BIT    : 0) |
22062306a36Sopenharmony_ci		((w->wr_low    & ORION_WR_LOW_EXT_MASK)    ? ORION_WR_LOW_EXT_BIT    : 0) |
22162306a36Sopenharmony_ci		((w->wr_high   & ORION_WR_HIGH_EXT_MASK)   ? ORION_WR_HIGH_EXT_BIT   : 0) |
22262306a36Sopenharmony_ci		(r->badr_skew << ORION_BADR_SKEW_SHIFT) |
22362306a36Sopenharmony_ci		ORION_RESERVED;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	writel(value, devbus->base);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void devbus_armada_set_timing_params(struct devbus *devbus,
22962306a36Sopenharmony_ci					   struct device_node *node,
23062306a36Sopenharmony_ci					   struct devbus_read_params *r,
23162306a36Sopenharmony_ci					   struct devbus_write_params *w)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	u32 value;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Set read timings */
23662306a36Sopenharmony_ci	value = r->bus_width << ARMADA_DEV_WIDTH_SHIFT |
23762306a36Sopenharmony_ci		r->badr_skew << ARMADA_BADR_SKEW_SHIFT |
23862306a36Sopenharmony_ci		r->rd_hold   << ARMADA_RD_HOLD_SHIFT   |
23962306a36Sopenharmony_ci		r->acc_next  << ARMADA_ACC_NEXT_SHIFT  |
24062306a36Sopenharmony_ci		r->rd_setup  << ARMADA_RD_SETUP_SHIFT  |
24162306a36Sopenharmony_ci		r->acc_first << ARMADA_ACC_FIRST_SHIFT |
24262306a36Sopenharmony_ci		r->turn_off;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n",
24562306a36Sopenharmony_ci		devbus->base + ARMADA_READ_PARAM_OFFSET,
24662306a36Sopenharmony_ci		value);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Set write timings */
25162306a36Sopenharmony_ci	value = w->sync_enable  << ARMADA_SYNC_ENABLE_SHIFT |
25262306a36Sopenharmony_ci		w->wr_low       << ARMADA_WR_LOW_SHIFT      |
25362306a36Sopenharmony_ci		w->wr_high      << ARMADA_WR_HIGH_SHIFT     |
25462306a36Sopenharmony_ci		w->ale_wr;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n",
25762306a36Sopenharmony_ci		devbus->base + ARMADA_WRITE_PARAM_OFFSET,
25862306a36Sopenharmony_ci		value);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	writel(value, devbus->base + ARMADA_WRITE_PARAM_OFFSET);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int mvebu_devbus_probe(struct platform_device *pdev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
26662306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
26762306a36Sopenharmony_ci	struct devbus_read_params r;
26862306a36Sopenharmony_ci	struct devbus_write_params w;
26962306a36Sopenharmony_ci	struct devbus *devbus;
27062306a36Sopenharmony_ci	struct clk *clk;
27162306a36Sopenharmony_ci	unsigned long rate;
27262306a36Sopenharmony_ci	int err;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL);
27562306a36Sopenharmony_ci	if (!devbus)
27662306a36Sopenharmony_ci		return -ENOMEM;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	devbus->dev = dev;
27962306a36Sopenharmony_ci	devbus->base = devm_platform_ioremap_resource(pdev, 0);
28062306a36Sopenharmony_ci	if (IS_ERR(devbus->base))
28162306a36Sopenharmony_ci		return PTR_ERR(devbus->base);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	clk = devm_clk_get_enabled(&pdev->dev, NULL);
28462306a36Sopenharmony_ci	if (IS_ERR(clk))
28562306a36Sopenharmony_ci		return PTR_ERR(clk);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * Obtain clock period in picoseconds,
28962306a36Sopenharmony_ci	 * we need this in order to convert timing
29062306a36Sopenharmony_ci	 * parameters from cycles to picoseconds.
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	rate = clk_get_rate(clk) / 1000;
29362306a36Sopenharmony_ci	devbus->tick_ps = 1000000000 / rate;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n",
29662306a36Sopenharmony_ci		devbus->tick_ps);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!of_property_read_bool(node, "devbus,keep-config")) {
29962306a36Sopenharmony_ci		/* Read the Device Tree node */
30062306a36Sopenharmony_ci		err = devbus_get_timing_params(devbus, node, &r, &w);
30162306a36Sopenharmony_ci		if (err < 0)
30262306a36Sopenharmony_ci			return err;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		/* Set the new timing parameters */
30562306a36Sopenharmony_ci		if (of_device_is_compatible(node, "marvell,orion-devbus"))
30662306a36Sopenharmony_ci			devbus_orion_set_timing_params(devbus, node, &r, &w);
30762306a36Sopenharmony_ci		else
30862306a36Sopenharmony_ci			devbus_armada_set_timing_params(devbus, node, &r, &w);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/*
31262306a36Sopenharmony_ci	 * We need to create a child device explicitly from here to
31362306a36Sopenharmony_ci	 * guarantee that the child will be probed after the timing
31462306a36Sopenharmony_ci	 * parameters for the bus are written.
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci	err = of_platform_populate(node, NULL, NULL, dev);
31762306a36Sopenharmony_ci	if (err < 0)
31862306a36Sopenharmony_ci		return err;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic const struct of_device_id mvebu_devbus_of_match[] = {
32462306a36Sopenharmony_ci	{ .compatible = "marvell,mvebu-devbus" },
32562306a36Sopenharmony_ci	{ .compatible = "marvell,orion-devbus" },
32662306a36Sopenharmony_ci	{},
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvebu_devbus_of_match);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic struct platform_driver mvebu_devbus_driver = {
33162306a36Sopenharmony_ci	.probe		= mvebu_devbus_probe,
33262306a36Sopenharmony_ci	.driver		= {
33362306a36Sopenharmony_ci		.name	= "mvebu-devbus",
33462306a36Sopenharmony_ci		.of_match_table = mvebu_devbus_of_match,
33562306a36Sopenharmony_ci	},
33662306a36Sopenharmony_ci};
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int __init mvebu_devbus_init(void)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	return platform_driver_register(&mvebu_devbus_driver);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_cimodule_init(mvebu_devbus_init);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciMODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
34562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");
346