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 ®, 4); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, sizeof(reg)); 16962306a36Sopenharmony_ci if (rc) 17062306a36Sopenharmony_ci return rc; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci rc = fsi_device_read(dev, FSI_MAEB, ®, 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, ®, sizeof(reg)); 17862306a36Sopenharmony_ci if (rc) 17962306a36Sopenharmony_ci return rc; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci rc = fsi_device_read(dev, FSI_MLEVP0, ®, 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, ®, 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, ®, 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