18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RapidIO Tsi568 switch support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2009-2010 Integrated Device Technology, Inc. 68c2ecf20Sopenharmony_ci * Alexandre Bounine <alexandre.bounine@idt.com> 78c2ecf20Sopenharmony_ci * - Added EM support 88c2ecf20Sopenharmony_ci * - Modified switch operations initialization. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright 2005 MontaVista Software, Inc. 118c2ecf20Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/rio.h> 158c2ecf20Sopenharmony_ci#include <linux/rio_drv.h> 168c2ecf20Sopenharmony_ci#include <linux/rio_ids.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include "../rio.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Global (broadcast) route registers */ 228c2ecf20Sopenharmony_ci#define SPBC_ROUTE_CFG_DESTID 0x10070 238c2ecf20Sopenharmony_ci#define SPBC_ROUTE_CFG_PORT 0x10074 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Per port route registers */ 268c2ecf20Sopenharmony_ci#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n) 278c2ecf20Sopenharmony_ci#define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TSI568_SP_MODE(n) (0x11004 + 0x100*n) 308c2ecf20Sopenharmony_ci#define TSI568_SP_MODE_PW_DIS 0x08000000 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int 338c2ecf20Sopenharmony_citsi568_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 348c2ecf20Sopenharmony_ci u16 table, u16 route_destid, u8 route_port) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 378c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 388c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_DESTID, route_destid); 398c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 408c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_PORT, route_port); 418c2ecf20Sopenharmony_ci } else { 428c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 438c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_DESTID(table), 448c2ecf20Sopenharmony_ci route_destid); 458c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 468c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_PORT(table), route_port); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci udelay(10); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int 558c2ecf20Sopenharmony_citsi568_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 568c2ecf20Sopenharmony_ci u16 table, u16 route_destid, u8 *route_port) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int ret = 0; 598c2ecf20Sopenharmony_ci u32 result; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 628c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 638c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_DESTID, route_destid); 648c2ecf20Sopenharmony_ci rio_mport_read_config_32(mport, destid, hopcount, 658c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_PORT, &result); 668c2ecf20Sopenharmony_ci } else { 678c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 688c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_DESTID(table), 698c2ecf20Sopenharmony_ci route_destid); 708c2ecf20Sopenharmony_ci rio_mport_read_config_32(mport, destid, hopcount, 718c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_PORT(table), &result); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci *route_port = result; 758c2ecf20Sopenharmony_ci if (*route_port > 15) 768c2ecf20Sopenharmony_ci ret = -1; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int 828c2ecf20Sopenharmony_citsi568_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, 838c2ecf20Sopenharmony_ci u16 table) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u32 route_idx; 868c2ecf20Sopenharmony_ci u32 lut_size; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci lut_size = (mport->sys_size) ? 0x1ff : 0xff; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (table == RIO_GLOBAL_TABLE) { 918c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 928c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_DESTID, 0x80000000); 938c2ecf20Sopenharmony_ci for (route_idx = 0; route_idx <= lut_size; route_idx++) 948c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 958c2ecf20Sopenharmony_ci SPBC_ROUTE_CFG_PORT, 968c2ecf20Sopenharmony_ci RIO_INVALID_ROUTE); 978c2ecf20Sopenharmony_ci } else { 988c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 998c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_DESTID(table), 1008c2ecf20Sopenharmony_ci 0x80000000); 1018c2ecf20Sopenharmony_ci for (route_idx = 0; route_idx <= lut_size; route_idx++) 1028c2ecf20Sopenharmony_ci rio_mport_write_config_32(mport, destid, hopcount, 1038c2ecf20Sopenharmony_ci SPP_ROUTE_CFG_PORT(table), 1048c2ecf20Sopenharmony_ci RIO_INVALID_ROUTE); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int 1118c2ecf20Sopenharmony_citsi568_em_init(struct rio_dev *rdev) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci u32 regval; 1148c2ecf20Sopenharmony_ci int portnum; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pr_debug("TSI568 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Make sure that Port-Writes are disabled (for all ports) */ 1198c2ecf20Sopenharmony_ci for (portnum = 0; 1208c2ecf20Sopenharmony_ci portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) { 1218c2ecf20Sopenharmony_ci rio_read_config_32(rdev, TSI568_SP_MODE(portnum), ®val); 1228c2ecf20Sopenharmony_ci rio_write_config_32(rdev, TSI568_SP_MODE(portnum), 1238c2ecf20Sopenharmony_ci regval | TSI568_SP_MODE_PW_DIS); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct rio_switch_ops tsi568_switch_ops = { 1308c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1318c2ecf20Sopenharmony_ci .add_entry = tsi568_route_add_entry, 1328c2ecf20Sopenharmony_ci .get_entry = tsi568_route_get_entry, 1338c2ecf20Sopenharmony_ci .clr_table = tsi568_route_clr_table, 1348c2ecf20Sopenharmony_ci .set_domain = NULL, 1358c2ecf20Sopenharmony_ci .get_domain = NULL, 1368c2ecf20Sopenharmony_ci .em_init = tsi568_em_init, 1378c2ecf20Sopenharmony_ci .em_handle = NULL, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int tsi568_probe(struct rio_dev *rdev, const struct rio_device_id *id) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock(&rdev->rswitch->lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (rdev->rswitch->ops) { 1478c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci rdev->rswitch->ops = &tsi568_switch_ops; 1528c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void tsi568_remove(struct rio_dev *rdev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 1598c2ecf20Sopenharmony_ci spin_lock(&rdev->rswitch->lock); 1608c2ecf20Sopenharmony_ci if (rdev->rswitch->ops != &tsi568_switch_ops) { 1618c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci rdev->rswitch->ops = NULL; 1658c2ecf20Sopenharmony_ci spin_unlock(&rdev->rswitch->lock); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct rio_device_id tsi568_id_table[] = { 1698c2ecf20Sopenharmony_ci {RIO_DEVICE(RIO_DID_TSI568, RIO_VID_TUNDRA)}, 1708c2ecf20Sopenharmony_ci { 0, } /* terminate list */ 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic struct rio_driver tsi568_driver = { 1748c2ecf20Sopenharmony_ci .name = "tsi568", 1758c2ecf20Sopenharmony_ci .id_table = tsi568_id_table, 1768c2ecf20Sopenharmony_ci .probe = tsi568_probe, 1778c2ecf20Sopenharmony_ci .remove = tsi568_remove, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int __init tsi568_init(void) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci return rio_register_driver(&tsi568_driver); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void __exit tsi568_exit(void) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci rio_unregister_driver(&tsi568_driver); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cidevice_initcall(tsi568_init); 1918c2ecf20Sopenharmony_cimodule_exit(tsi568_exit); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IDT Tsi568 Serial RapidIO switch driver"); 1948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Integrated Device Technology, Inc."); 1958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 196