162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * FSI hub master driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) IBM Corporation 2016
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/fsi.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "fsi-master.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define FSI_ENGID_HUB_MASTER		0x1c
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define FSI_LINK_ENABLE_SETUP_TIME	10	/* in mS */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * FSI hub master support
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * A hub master increases the number of potential target devices that the
2462306a36Sopenharmony_ci * primary FSI master can access. For each link a primary master supports,
2562306a36Sopenharmony_ci * each of those links can in turn be chained to a hub master with multiple
2662306a36Sopenharmony_ci * links of its own.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * The hub is controlled by a set of control registers exposed as a regular fsi
2962306a36Sopenharmony_ci * device (the hub->upstream device), and provides access to the downstream FSI
3062306a36Sopenharmony_ci * bus as through an address range on the slave itself (->addr and ->size).
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * [This differs from "cascaded" masters, which expose the entire downstream
3362306a36Sopenharmony_ci * bus entirely through the fsi device address range, and so have a smaller
3462306a36Sopenharmony_ci * accessible address space.]
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistruct fsi_master_hub {
3762306a36Sopenharmony_ci	struct fsi_master	master;
3862306a36Sopenharmony_ci	struct fsi_device	*upstream;
3962306a36Sopenharmony_ci	uint32_t		addr, size;	/* slave-relative addr of */
4062306a36Sopenharmony_ci						/* master address space */
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int hub_master_read(struct fsi_master *master, int link,
4662306a36Sopenharmony_ci			uint8_t id, uint32_t addr, void *val, size_t size)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct fsi_master_hub *hub = to_fsi_master_hub(master);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (id != 0)
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
5462306a36Sopenharmony_ci	return fsi_slave_read(hub->upstream->slave, addr, val, size);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int hub_master_write(struct fsi_master *master, int link,
5862306a36Sopenharmony_ci			uint8_t id, uint32_t addr, const void *val, size_t size)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct fsi_master_hub *hub = to_fsi_master_hub(master);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (id != 0)
6362306a36Sopenharmony_ci		return -EINVAL;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
6662306a36Sopenharmony_ci	return fsi_slave_write(hub->upstream->slave, addr, val, size);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int hub_master_break(struct fsi_master *master, int link)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	uint32_t addr;
7262306a36Sopenharmony_ci	__be32 cmd;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	addr = 0x4;
7562306a36Sopenharmony_ci	cmd = cpu_to_be32(0xc0de0000);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int hub_master_link_enable(struct fsi_master *master, int link,
8162306a36Sopenharmony_ci				  bool enable)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct fsi_master_hub *hub = to_fsi_master_hub(master);
8462306a36Sopenharmony_ci	int idx, bit;
8562306a36Sopenharmony_ci	__be32 reg;
8662306a36Sopenharmony_ci	int rc;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	idx = link / 32;
8962306a36Sopenharmony_ci	bit = link % 32;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	reg = cpu_to_be32(0x80000000 >> bit);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!enable)
9462306a36Sopenharmony_ci		return fsi_device_write(hub->upstream, FSI_MCENP0 + (4 * idx),
9562306a36Sopenharmony_ci					&reg, 4);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
9862306a36Sopenharmony_ci	if (rc)
9962306a36Sopenharmony_ci		return rc;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	mdelay(FSI_LINK_ENABLE_SETUP_TIME);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void hub_master_release(struct device *dev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	kfree(hub);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* mmode encoders */
11462306a36Sopenharmony_cistatic inline u32 fsi_mmode_crs0(u32 x)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic inline u32 fsi_mmode_crs1(u32 x)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int hub_master_init(struct fsi_master_hub *hub)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct fsi_device *dev = hub->upstream;
12762306a36Sopenharmony_ci	__be32 reg;
12862306a36Sopenharmony_ci	int rc;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
13162306a36Sopenharmony_ci			| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
13262306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
13362306a36Sopenharmony_ci	if (rc)
13462306a36Sopenharmony_ci		return rc;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Initialize the MFSI (hub master) engine */
13762306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
13862306a36Sopenharmony_ci			| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
13962306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
14062306a36Sopenharmony_ci	if (rc)
14162306a36Sopenharmony_ci		return rc;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
14462306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MECTRL, &reg, sizeof(reg));
14562306a36Sopenharmony_ci	if (rc)
14662306a36Sopenharmony_ci		return rc;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
14962306a36Sopenharmony_ci			| fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
15062306a36Sopenharmony_ci			| FSI_MMODE_P8_TO_LSB);
15162306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MMODE, &reg, sizeof(reg));
15262306a36Sopenharmony_ci	if (rc)
15362306a36Sopenharmony_ci		return rc;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	reg = cpu_to_be32(0xffff0000);
15662306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MDLYR, &reg, sizeof(reg));
15762306a36Sopenharmony_ci	if (rc)
15862306a36Sopenharmony_ci		return rc;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	reg = cpu_to_be32(~0);
16162306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MSENP0, &reg, sizeof(reg));
16262306a36Sopenharmony_ci	if (rc)
16362306a36Sopenharmony_ci		return rc;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Leave enabled long enough for master logic to set up */
16662306a36Sopenharmony_ci	mdelay(FSI_LINK_ENABLE_SETUP_TIME);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MCENP0, &reg, sizeof(reg));
16962306a36Sopenharmony_ci	if (rc)
17062306a36Sopenharmony_ci		return rc;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	rc = fsi_device_read(dev, FSI_MAEB, &reg, sizeof(reg));
17362306a36Sopenharmony_ci	if (rc)
17462306a36Sopenharmony_ci		return rc;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
17762306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
17862306a36Sopenharmony_ci	if (rc)
17962306a36Sopenharmony_ci		return rc;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	rc = fsi_device_read(dev, FSI_MLEVP0, &reg, sizeof(reg));
18262306a36Sopenharmony_ci	if (rc)
18362306a36Sopenharmony_ci		return rc;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* Reset the master bridge */
18662306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MRESB_RST_GEN);
18762306a36Sopenharmony_ci	rc = fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
18862306a36Sopenharmony_ci	if (rc)
18962306a36Sopenharmony_ci		return rc;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	reg = cpu_to_be32(FSI_MRESB_RST_ERR);
19262306a36Sopenharmony_ci	return fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int hub_master_probe(struct device *dev)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct fsi_device *fsi_dev = to_fsi_dev(dev);
19862306a36Sopenharmony_ci	struct fsi_master_hub *hub;
19962306a36Sopenharmony_ci	uint32_t reg, links;
20062306a36Sopenharmony_ci	__be32 __reg;
20162306a36Sopenharmony_ci	int rc;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
20462306a36Sopenharmony_ci	if (rc)
20562306a36Sopenharmony_ci		return rc;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	reg = be32_to_cpu(__reg);
20862306a36Sopenharmony_ci	links = (reg >> 8) & 0xff;
20962306a36Sopenharmony_ci	dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
21262306a36Sopenharmony_ci			FSI_HUB_LINK_SIZE * links);
21362306a36Sopenharmony_ci	if (rc) {
21462306a36Sopenharmony_ci		dev_err(dev, "can't claim slave address range for links");
21562306a36Sopenharmony_ci		return rc;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	hub = kzalloc(sizeof(*hub), GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!hub) {
22062306a36Sopenharmony_ci		rc = -ENOMEM;
22162306a36Sopenharmony_ci		goto err_release;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	hub->addr = FSI_HUB_LINK_OFFSET;
22562306a36Sopenharmony_ci	hub->size = FSI_HUB_LINK_SIZE * links;
22662306a36Sopenharmony_ci	hub->upstream = fsi_dev;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	hub->master.dev.parent = dev;
22962306a36Sopenharmony_ci	hub->master.dev.release = hub_master_release;
23062306a36Sopenharmony_ci	hub->master.dev.of_node = of_node_get(dev_of_node(dev));
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	hub->master.n_links = links;
23362306a36Sopenharmony_ci	hub->master.read = hub_master_read;
23462306a36Sopenharmony_ci	hub->master.write = hub_master_write;
23562306a36Sopenharmony_ci	hub->master.send_break = hub_master_break;
23662306a36Sopenharmony_ci	hub->master.link_enable = hub_master_link_enable;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	dev_set_drvdata(dev, hub);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	hub_master_init(hub);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	rc = fsi_master_register(&hub->master);
24362306a36Sopenharmony_ci	if (rc)
24462306a36Sopenharmony_ci		goto err_release;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* At this point, fsi_master_register performs the device_initialize(),
24762306a36Sopenharmony_ci	 * and holds the sole reference on master.dev. This means the device
24862306a36Sopenharmony_ci	 * will be freed (via ->release) during any subsequent call to
24962306a36Sopenharmony_ci	 * fsi_master_unregister.  We add our own reference to it here, so we
25062306a36Sopenharmony_ci	 * can perform cleanup (in _remove()) without it being freed before
25162306a36Sopenharmony_ci	 * we're ready.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	get_device(&hub->master.dev);
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cierr_release:
25762306a36Sopenharmony_ci	fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
25862306a36Sopenharmony_ci			FSI_HUB_LINK_SIZE * links);
25962306a36Sopenharmony_ci	return rc;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int hub_master_remove(struct device *dev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct fsi_master_hub *hub = dev_get_drvdata(dev);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	fsi_master_unregister(&hub->master);
26762306a36Sopenharmony_ci	fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
26862306a36Sopenharmony_ci	of_node_put(hub->master.dev.of_node);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/*
27162306a36Sopenharmony_ci	 * master.dev will likely be ->release()ed after this, which free()s
27262306a36Sopenharmony_ci	 * the hub
27362306a36Sopenharmony_ci	 */
27462306a36Sopenharmony_ci	put_device(&hub->master.dev);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic const struct fsi_device_id hub_master_ids[] = {
28062306a36Sopenharmony_ci	{
28162306a36Sopenharmony_ci		.engine_type = FSI_ENGID_HUB_MASTER,
28262306a36Sopenharmony_ci		.version = FSI_VERSION_ANY,
28362306a36Sopenharmony_ci	},
28462306a36Sopenharmony_ci	{ 0 }
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct fsi_driver hub_master_driver = {
28862306a36Sopenharmony_ci	.id_table = hub_master_ids,
28962306a36Sopenharmony_ci	.drv = {
29062306a36Sopenharmony_ci		.name = "fsi-master-hub",
29162306a36Sopenharmony_ci		.bus = &fsi_bus_type,
29262306a36Sopenharmony_ci		.probe = hub_master_probe,
29362306a36Sopenharmony_ci		.remove = hub_master_remove,
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cimodule_fsi_driver(hub_master_driver);
29862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
299