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, &regval);
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