1#!/usr/bin/python
2
3# Python library for in-band SCom access
4# (based on xscom-utils from OPAL firmware)
5#
6# Copyright 2018 IBM Corp.
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12#	http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17# implied.
18# See the License for the specific language governing permissions and
19# limitations under the License.
20
21import os, sys, struct, getopt
22
23class XSCom(object):
24	def __init__(self):
25		self.name = "xscom"
26		self.base = "/sys/kernel/debug/powerpc/scom/"
27		self.enabled = False
28		self.setup = False
29		self.chips = []
30		self.dirs = []
31		self.key_val_bin = {}
32		self.file = "/access"
33
34		if os.path.exists(self.base):
35			self.enabled = True
36
37		if not self.scan_chips():
38			raise ValueError
39
40	def scan_chips(self):
41		if not self.enabled:
42			print("Not supported")
43			return False
44
45		for i in os.listdir(self.base):
46			if os.path.isdir(self.base+i):
47				self.dirs.append(i)
48				self.chips.append(int(i,16))
49
50		for i in  self.dirs:
51			try:
52				b = open(self.base+i+self.file, "rb+")
53				self.key_val_bin[int(i,16)] = b
54			except:
55				print("Count not open"+self.base+i+self.file)
56				return False
57
58		self.setup = True
59		return True
60
61	def is_supported(self):
62		return self.enabled
63
64	def get_chip_ids(self):
65		return list(self.key_val_bin.keys())
66
67	def mangle_addr(self, addr):
68		tmp = (addr & 0xf000000000000000) >> 4
69		addr = (addr & 0x00ffffffffffffff)
70		addr = addr | tmp
71		return (addr << 3)
72
73	def xscom_read(self, chip_id, addr):
74		if not isinstance(chip_id, int) or not isinstance(addr, int):
75			print("xscom_read: Input paramater type mismatch")
76			return -1
77
78		if chip_id not in self.key_val_bin:
79			print("Invalid Chip id")
80			return -1
81
82		saddr = self.mangle_addr(addr)
83		fd = self.key_val_bin.get(chip_id)
84		fd.seek(saddr, 0)
85		return struct.unpack('Q',fd.read(8))[0]
86
87	def xscom_read_spl(self, chip_id, addr):
88		if not isinstance(chip_id, int) or not isinstance(addr, int):
89			print("xscom_read: Input paramater type mismatch")
90			return -1
91
92		if chip_id not in self.key_val_bin:
93			print("Invalid Chip id")
94			return -1
95
96		saddr = self.mangle_addr(addr)
97		fd = self.key_val_bin.get(chip_id)
98		fd.seek(saddr, 0)
99		val = struct.unpack('Q',fd.read(8))[0]
100		fd.close()
101		try:
102			b = open(self.key_val_path.get(chip_id), "rb+")
103		except:
104			print("Reopen failed")
105			return val
106		self.key_val_bin[chip_id] = b
107		return val
108
109	def xscom_write(self, chip_id, addr, val):
110		if chip_id not in self.key_val_bin:
111			print("Invalid Chip id")
112			return -1
113
114		c = struct.pack('Q',val)
115		saddr = self.mangle_addr(addr)
116		fd = self.key_val_bin.get(chip_id)
117
118		try:
119			fd.seek(saddr, 0)
120			fd.write(c)
121			# write again just to be sure
122			fd.seek(saddr, 0)
123			fd.write(c)
124		except:
125			print("Write() error")
126			return -1
127
128	def xscom_read_ex(self, ex_target_id, addr):
129		if not isinstance(ex_target_id, int) or not isinstance(addr, int):
130			print("xscom_read_ex: Input paramater type mismatch")
131			return -1
132
133		chip_id = ex_target_id >> 4
134		addr |= (ex_target_id & 0xf) << 24;
135		return self.xscom_read(chip_id, addr, val);
136
137	def xscom_write_ex(self, ex_target_id, addr, val):
138		chip_id = ex_target_id >> 4
139		addr |= (ex_target_id & 0xf) << 24;
140		return self.xscom_write(chip_id, addr, val)
141
142class GetSCom(object):
143	def __init__(self):
144		self.name = "getscom"
145		self.backend = XSCom()
146		self.listchip = False
147		self.chip_id = 0
148		self.chips = False
149		self.addr = 0
150		self.flg_addr = False
151
152		if not self.backend.is_supported():
153			print("In-Band SCom not supported Exiting....")
154			raise ValueError
155
156	def set_chip(self, chip_id):
157		self.chip_id = chip_id
158		self.chips = True
159
160	def set_addr(self, scom_addr):
161		self.addr = scom_addr
162		self.flg_addr = True
163
164	def print_usage(self):
165		print("usage: getscom [-c|--chip chip-id] addr")
166		print("       getscom -l|--list-chips")
167		print("       getscom -h|--help")
168		sys.exit(0)
169
170
171	def chip_info(self, chip_id):
172		val = self.backend.xscom_read(chip_id, 0xf000f)
173		if val < 0:
174			print("Error in scom read")
175			raise ValueError
176
177		c_id = val >> 44
178		id = c_id & 0xff
179                if id == 0xef:
180			name = "P8E (Murano) processor"
181		elif id == 0xea:
182			name = "P8 (Venice) processor"
183		elif id == 0xd3:
184			name = "P8NVL (Naples) processor"
185		elif id == 0xd1:
186			name = "P9 (Nimbus) processor"
187		elif id == 0xd4:
188			name = "P9 (Cumulus) processor"
189		elif id == 0xe9:
190			name = "Centaur memory buffer"
191		else:
192			name = "Unknown ID 0x%x"%id
193
194		print(("%08x | DD%s.%s | %s"%(chip_id, ((c_id >> 16) & 0xf), ((c_id >> 8) & 0xf), name)))
195
196	def parse_args(self):
197		try:
198			optlist, sys.argv = getopt.getopt(sys.argv[1:], "lhc:", ["chip", "list-chips", "help"])
199		except getopt.GetoptError as err:
200			print(str(err))
201			self.print_usage()
202			sys.exit(0)
203
204		if len(optlist) == 0:
205			self.print_usage()
206			sys.exit(0)
207
208		for opt, arg in optlist:
209			if opt in [ "-h", "--help"]:
210				self.print_usage()
211				sys.exit(0)
212
213			elif opt in [ "-l", "--list-chips"]:
214				self.listchip = True
215
216			elif opt in ["-c", "--chip"]:
217				self.chip_id = int(arg, 16)
218				self.chips = True
219
220		if sys.argv:
221			self.addr =  int(sys.argv.pop(), 16)
222			self.flg_addr = True
223
224		if self.listchip:
225			print("Chip ID  | Rev   | Chip type")
226			print("---------|-------|-----------")
227			for i in self.backend.get_chip_ids():
228				self.chip_info(i)
229
230			sys.exit(0)
231
232	def run_command(self):
233		if self.chips and self.flg_addr:
234			print(hex(self.backend.xscom_read(self.chip_id, self.addr)))
235
236	def list_chips(self):
237		print("Chip ID  | Rev   | Chip type")
238		print("---------|-------|-----------")
239		for i in self.backend.get_chip_ids():
240			self.chip_info(i)
241
242		raise ValueError
243
244	def execute(self, chip_id, addr):
245		return self.backend.xscom_read(chip_id, addr)
246
247	def execute_spl(self, chip_id, addr):
248		return self.backend.xscom_read_spl(chip_id, addr)
249
250class PutSCom(object):
251	def __init__(self):
252		self.name = "putscom"
253		self.backend = XSCom()
254		self.chip_id = 0
255		self.chips = False
256		self.addr = 0
257		self.value = 0
258
259		if not self.backend.is_supported():
260			print("In-Band SCom not supported Exiting....")
261			raise ValueError
262
263	def set_addr(self, addr):
264		self.addr = addr
265
266	def set_value(self, value):
267		self.value = value
268
269	def print_usage(self):
270		print("usage: putscom [-c|--chip chip-id] addr value")
271		print("       putscom -h|--help")
272		sys.exit(0)
273
274	def parse_args(self):
275		try:
276			optlist, sys.argv = getopt.getopt(sys.argv[1:], "hc:", ["chip", "help"])
277		except getopt.GetoptError as err:
278			print(str(err))
279			self.print_usage()
280			sys.exit(0)
281
282		if len(optlist) == 0:
283			self.print_usage()
284			sys.exit(0)
285
286		for opt, arg in optlist:
287			if opt in [ "-h", "--help"]:
288				self.print_usage()
289				sys.exit(0)
290
291			elif opt in ["-c", "--chip"]:
292				self.chip_id = int(arg, 16)
293				self.chips = True
294
295		if sys.argv:
296			self.value =  int(sys.argv.pop(), 16)
297			self.addr =  int(sys.argv.pop(), 16)
298
299		if self.chips:
300			self.backend.xscom_write(self.chip_id, self.addr, self.value)
301
302	def run_command(self):
303		if self.chips:
304			self.backend.xscom_write(self.chip_id, self.addr, self.value)
305
306	def execute(self, chip_id, addr, value):
307		self.backend.xscom_write(chip_id, addr, value)
308
309