1/* 2 * IDT CPS RapidIO switches support 3 * 4 * Copyright 2009-2010 Integrated Device Technology, Inc. 5 * Alexandre Bounine <alexandre.bounine@idt.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13#include <linux/rio.h> 14#include <linux/rio_drv.h> 15#include <linux/rio_ids.h> 16#include <linux/module.h> 17#include "../rio.h" 18 19#define CPS_DEFAULT_ROUTE 0xde 20#define CPS_NO_ROUTE 0xdf 21 22#define IDTCPS_RIO_DOMAIN 0xf20020 23 24static int 25idtcps_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 26 u16 table, u16 route_destid, u8 route_port) 27{ 28 u32 result; 29 30 if (route_port == RIO_INVALID_ROUTE) 31 route_port = CPS_DEFAULT_ROUTE; 32 33 if (table == RIO_GLOBAL_TABLE) { 34 rio_mport_write_config_32(mport, destid, hopcount, 35 RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); 36 37 rio_mport_read_config_32(mport, destid, hopcount, 38 RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); 39 40 result = (0xffffff00 & result) | (u32)route_port; 41 rio_mport_write_config_32(mport, destid, hopcount, 42 RIO_STD_RTE_CONF_PORT_SEL_CSR, result); 43 } 44 45 return 0; 46} 47 48static int 49idtcps_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, 50 u16 table, u16 route_destid, u8 *route_port) 51{ 52 u32 result; 53 54 if (table == RIO_GLOBAL_TABLE) { 55 rio_mport_write_config_32(mport, destid, hopcount, 56 RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); 57 58 rio_mport_read_config_32(mport, destid, hopcount, 59 RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); 60 61 if (CPS_DEFAULT_ROUTE == (u8)result || 62 CPS_NO_ROUTE == (u8)result) 63 *route_port = RIO_INVALID_ROUTE; 64 else 65 *route_port = (u8)result; 66 } 67 68 return 0; 69} 70 71static int 72idtcps_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, 73 u16 table) 74{ 75 u32 i; 76 77 if (table == RIO_GLOBAL_TABLE) { 78 for (i = 0x80000000; i <= 0x800000ff;) { 79 rio_mport_write_config_32(mport, destid, hopcount, 80 RIO_STD_RTE_CONF_DESTID_SEL_CSR, i); 81 rio_mport_write_config_32(mport, destid, hopcount, 82 RIO_STD_RTE_CONF_PORT_SEL_CSR, 83 (CPS_DEFAULT_ROUTE << 24) | 84 (CPS_DEFAULT_ROUTE << 16) | 85 (CPS_DEFAULT_ROUTE << 8) | CPS_DEFAULT_ROUTE); 86 i += 4; 87 } 88 } 89 90 return 0; 91} 92 93static int 94idtcps_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount, 95 u8 sw_domain) 96{ 97 /* 98 * Switch domain configuration operates only at global level 99 */ 100 rio_mport_write_config_32(mport, destid, hopcount, 101 IDTCPS_RIO_DOMAIN, (u32)sw_domain); 102 return 0; 103} 104 105static int 106idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, 107 u8 *sw_domain) 108{ 109 u32 regval; 110 111 /* 112 * Switch domain configuration operates only at global level 113 */ 114 rio_mport_read_config_32(mport, destid, hopcount, 115 IDTCPS_RIO_DOMAIN, ®val); 116 117 *sw_domain = (u8)(regval & 0xff); 118 119 return 0; 120} 121 122static struct rio_switch_ops idtcps_switch_ops = { 123 .owner = THIS_MODULE, 124 .add_entry = idtcps_route_add_entry, 125 .get_entry = idtcps_route_get_entry, 126 .clr_table = idtcps_route_clr_table, 127 .set_domain = idtcps_set_domain, 128 .get_domain = idtcps_get_domain, 129 .em_init = NULL, 130 .em_handle = NULL, 131}; 132 133static int idtcps_probe(struct rio_dev *rdev, const struct rio_device_id *id) 134{ 135 pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 136 137 spin_lock(&rdev->rswitch->lock); 138 139 if (rdev->rswitch->ops) { 140 spin_unlock(&rdev->rswitch->lock); 141 return -EINVAL; 142 } 143 144 rdev->rswitch->ops = &idtcps_switch_ops; 145 146 if (rdev->do_enum) { 147 /* set TVAL = ~50us */ 148 rio_write_config_32(rdev, 149 rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); 150 /* Ensure that default routing is disabled on startup */ 151 rio_write_config_32(rdev, 152 RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE); 153 } 154 155 spin_unlock(&rdev->rswitch->lock); 156 return 0; 157} 158 159static void idtcps_remove(struct rio_dev *rdev) 160{ 161 pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); 162 spin_lock(&rdev->rswitch->lock); 163 if (rdev->rswitch->ops != &idtcps_switch_ops) { 164 spin_unlock(&rdev->rswitch->lock); 165 return; 166 } 167 rdev->rswitch->ops = NULL; 168 spin_unlock(&rdev->rswitch->lock); 169} 170 171static struct rio_device_id idtcps_id_table[] = { 172 {RIO_DEVICE(RIO_DID_IDTCPS6Q, RIO_VID_IDT)}, 173 {RIO_DEVICE(RIO_DID_IDTCPS8, RIO_VID_IDT)}, 174 {RIO_DEVICE(RIO_DID_IDTCPS10Q, RIO_VID_IDT)}, 175 {RIO_DEVICE(RIO_DID_IDTCPS12, RIO_VID_IDT)}, 176 {RIO_DEVICE(RIO_DID_IDTCPS16, RIO_VID_IDT)}, 177 {RIO_DEVICE(RIO_DID_IDT70K200, RIO_VID_IDT)}, 178 { 0, } /* terminate list */ 179}; 180 181static struct rio_driver idtcps_driver = { 182 .name = "idtcps", 183 .id_table = idtcps_id_table, 184 .probe = idtcps_probe, 185 .remove = idtcps_remove, 186}; 187 188static int __init idtcps_init(void) 189{ 190 return rio_register_driver(&idtcps_driver); 191} 192 193static void __exit idtcps_exit(void) 194{ 195 rio_unregister_driver(&idtcps_driver); 196} 197 198device_initcall(idtcps_init); 199module_exit(idtcps_exit); 200 201MODULE_DESCRIPTION("IDT CPS Gen.1 Serial RapidIO switch family driver"); 202MODULE_AUTHOR("Integrated Device Technology, Inc."); 203MODULE_LICENSE("GPL"); 204