18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IDT CPS RapidIO switches support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2009-2010 Integrated Device Technology, Inc. 68c2ecf20Sopenharmony_ci * Alexandre Bounine <alexandre.bounine@idt.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/rio.h> 108c2ecf20Sopenharmony_ci#include <linux/rio_drv.h> 118c2ecf20Sopenharmony_ci#include <linux/rio_ids.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include "../rio.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define CPS_DEFAULT_ROUTE 0xde 168c2ecf20Sopenharmony_ci#define CPS_NO_ROUTE 0xdf 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define IDTCPS_RIO_DOMAIN 0xf20020 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int 218c2ecf20Sopenharmony_ciidtcps_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 228c2ecf20Sopenharmony_ci u16 table, u16 route_destid, u8 route_port) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci u32 result; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (route_port == RIO_INVALID_ROUTE) 278c2ecf20Sopenharmony_ci route_port = CPS_DEFAULT_ROUTE; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 308c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 318c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci rio_mport_read_config_32(mport, destid, hopcount, 348c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci result = (0xffffff00 & result) | (u32)route_port; 378c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 388c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_PORT_SEL_CSR, result); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int 458c2ecf20Sopenharmony_ciidtcps_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 468c2ecf20Sopenharmony_ci u16 table, u16 route_destid, u8 *route_port) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci u32 result; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 518c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 528c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci rio_mport_read_config_32(mport, destid, hopcount, 558c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (CPS_DEFAULT_ROUTE == (u8)result || 588c2ecf20Sopenharmony_ci CPS_NO_ROUTE == (u8)result) 598c2ecf20Sopenharmony_ci *route_port = RIO_INVALID_ROUTE; 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci *route_port = (u8)result; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int 688c2ecf20Sopenharmony_ciidtcps_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, 698c2ecf20Sopenharmony_ci u16 table) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 i; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 748c2ecf20Sopenharmony_ci for (i = 0x80000000; i <= 0x800000ff;) { 758c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 768c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_DESTID_SEL_CSR, i); 778c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 788c2ecf20Sopenharmony_ci RIO_STD_RTE_CONF_PORT_SEL_CSR, 798c2ecf20Sopenharmony_ci (CPS_DEFAULT_ROUTE << 24) | 808c2ecf20Sopenharmony_ci (CPS_DEFAULT_ROUTE << 16) | 818c2ecf20Sopenharmony_ci (CPS_DEFAULT_ROUTE << 8) | CPS_DEFAULT_ROUTE); 828c2ecf20Sopenharmony_ci i += 4; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int 908c2ecf20Sopenharmony_ciidtcps_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount, 918c2ecf20Sopenharmony_ci u8 sw_domain) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Switch domain configuration operates only at global level 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 978c2ecf20Sopenharmony_ci IDTCPS_RIO_DOMAIN, (u32)sw_domain); 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int 1028c2ecf20Sopenharmony_ciidtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, 1038c2ecf20Sopenharmony_ci u8 *sw_domain) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 regval; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* 1088c2ecf20Sopenharmony_ci * Switch domain configuration operates only at global level 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci rio_mport_read_config_32(mport, destid, hopcount, 1118c2ecf20Sopenharmony_ci IDTCPS_RIO_DOMAIN, ®val); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *sw_domain = (u8)(regval & 0xff); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct rio_switch_ops idtcps_switch_ops = { 1198c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1208c2ecf20Sopenharmony_ci .add_entry = idtcps_route_add_entry, 1218c2ecf20Sopenharmony_ci .get_entry = idtcps_route_get_entry, 1228c2ecf20Sopenharmony_ci .clr_table = idtcps_route_clr_table, 1238c2ecf20Sopenharmony_ci .set_domain = idtcps_set_domain, 1248c2ecf20Sopenharmony_ci .get_domain = idtcps_get_domain, 1258c2ecf20Sopenharmony_ci .em_init = NULL, 1268c2ecf20Sopenharmony_ci .em_handle = NULL, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int idtcps_probe(struct rio_dev *rdev, const struct rio_device_id *id) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci spin_lock(&rdev->rswitch->lock); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (rdev->rswitch->ops) { 1368c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci rdev->rswitch->ops = &idtcps_switch_ops; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (rdev->do_enum) { 1438c2ecf20Sopenharmony_ci /* set TVAL = ~50us */ 1448c2ecf20Sopenharmony_ci rio_write_config_32(rdev, 1458c2ecf20Sopenharmony_ci rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); 1468c2ecf20Sopenharmony_ci /* Ensure that default routing is disabled on startup */ 1478c2ecf20Sopenharmony_ci rio_write_config_32(rdev, 1488c2ecf20Sopenharmony_ci RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void idtcps_remove(struct rio_dev *rdev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 1588c2ecf20Sopenharmony_ci spin_lock(&rdev->rswitch->lock); 1598c2ecf20Sopenharmony_ci if (rdev->rswitch->ops != &idtcps_switch_ops) { 1608c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci rdev->rswitch->ops = NULL; 1648c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct rio_device_id idtcps_id_table[] = { 1688c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDTCPS6Q, RIO_VID_IDT)}, 1698c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDTCPS8, RIO_VID_IDT)}, 1708c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDTCPS10Q, RIO_VID_IDT)}, 1718c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDTCPS12, RIO_VID_IDT)}, 1728c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDTCPS16, RIO_VID_IDT)}, 1738c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_IDT70K200, RIO_VID_IDT)}, 1748c2ecf20Sopenharmony_ci { 0, } /* terminate list */ 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct rio_driver idtcps_driver = { 1788c2ecf20Sopenharmony_ci .name = "idtcps", 1798c2ecf20Sopenharmony_ci .id_table = idtcps_id_table, 1808c2ecf20Sopenharmony_ci .probe = idtcps_probe, 1818c2ecf20Sopenharmony_ci .remove = idtcps_remove, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int __init idtcps_init(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci return rio_register_driver(&idtcps_driver); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void __exit idtcps_exit(void) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci rio_unregister_driver(&idtcps_driver); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cidevice_initcall(idtcps_init); 1958c2ecf20Sopenharmony_cimodule_exit(idtcps_exit); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IDT CPS Gen.1 Serial RapidIO switch family driver"); 1988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Integrated Device Technology, Inc."); 1998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 200