18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci// Copyright(c) 2015-2020 Intel Corporation.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/device.h>
58c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
88c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw.h>
98c2ecf20Sopenharmony_ci#include <linux/soundwire/sdw_type.h>
108c2ecf20Sopenharmony_ci#include "bus.h"
118c2ecf20Sopenharmony_ci#include "sysfs_local.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Slave sysfs
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * The sysfs for Slave reflects the MIPI description as given
198c2ecf20Sopenharmony_ci * in the MIPI DisCo spec.
208c2ecf20Sopenharmony_ci * status and device_number come directly from the MIPI SoundWire
218c2ecf20Sopenharmony_ci * 1.x specification.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Base file is device
248c2ecf20Sopenharmony_ci *	|---- status
258c2ecf20Sopenharmony_ci *	|---- device_number
268c2ecf20Sopenharmony_ci *	|---- modalias
278c2ecf20Sopenharmony_ci *	|---- dev-properties
288c2ecf20Sopenharmony_ci *		|---- mipi_revision
298c2ecf20Sopenharmony_ci *		|---- wake_capable
308c2ecf20Sopenharmony_ci *		|---- test_mode_capable
318c2ecf20Sopenharmony_ci *		|---- clk_stop_mode1
328c2ecf20Sopenharmony_ci *		|---- simple_clk_stop_capable
338c2ecf20Sopenharmony_ci *		|---- clk_stop_timeout
348c2ecf20Sopenharmony_ci *		|---- ch_prep_timeout
358c2ecf20Sopenharmony_ci *		|---- reset_behave
368c2ecf20Sopenharmony_ci *		|---- high_PHY_capable
378c2ecf20Sopenharmony_ci *		|---- paging_support
388c2ecf20Sopenharmony_ci *		|---- bank_delay_support
398c2ecf20Sopenharmony_ci *		|---- p15_behave
408c2ecf20Sopenharmony_ci *		|---- master_count
418c2ecf20Sopenharmony_ci *		|---- source_ports
428c2ecf20Sopenharmony_ci *		|---- sink_ports
438c2ecf20Sopenharmony_ci *	|---- dp0
448c2ecf20Sopenharmony_ci *		|---- max_word
458c2ecf20Sopenharmony_ci *		|---- min_word
468c2ecf20Sopenharmony_ci *		|---- words
478c2ecf20Sopenharmony_ci *		|---- BRA_flow_controlled
488c2ecf20Sopenharmony_ci *		|---- simple_ch_prep_sm
498c2ecf20Sopenharmony_ci *		|---- imp_def_interrupts
508c2ecf20Sopenharmony_ci *	|---- dpN_<sink/src>
518c2ecf20Sopenharmony_ci *		|---- max_word
528c2ecf20Sopenharmony_ci *		|---- min_word
538c2ecf20Sopenharmony_ci *		|---- words
548c2ecf20Sopenharmony_ci *		|---- type
558c2ecf20Sopenharmony_ci *		|---- max_grouping
568c2ecf20Sopenharmony_ci *		|---- simple_ch_prep_sm
578c2ecf20Sopenharmony_ci *		|---- ch_prep_timeout
588c2ecf20Sopenharmony_ci *		|---- imp_def_interrupts
598c2ecf20Sopenharmony_ci *		|---- min_ch
608c2ecf20Sopenharmony_ci *		|---- max_ch
618c2ecf20Sopenharmony_ci *		|---- channels
628c2ecf20Sopenharmony_ci *		|---- ch_combinations
638c2ecf20Sopenharmony_ci *		|---- max_async_buffer
648c2ecf20Sopenharmony_ci *		|---- block_pack_mode
658c2ecf20Sopenharmony_ci *		|---- port_encoding
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define sdw_slave_attr(field, format_string)			\
708c2ecf20Sopenharmony_cistatic ssize_t field##_show(struct device *dev,			\
718c2ecf20Sopenharmony_ci			    struct device_attribute *attr,	\
728c2ecf20Sopenharmony_ci			    char *buf)				\
738c2ecf20Sopenharmony_ci{								\
748c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);		\
758c2ecf20Sopenharmony_ci	return sprintf(buf, format_string, slave->prop.field);	\
768c2ecf20Sopenharmony_ci}								\
778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cisdw_slave_attr(mipi_revision, "0x%x\n");
808c2ecf20Sopenharmony_cisdw_slave_attr(wake_capable, "%d\n");
818c2ecf20Sopenharmony_cisdw_slave_attr(test_mode_capable, "%d\n");
828c2ecf20Sopenharmony_cisdw_slave_attr(clk_stop_mode1, "%d\n");
838c2ecf20Sopenharmony_cisdw_slave_attr(simple_clk_stop_capable, "%d\n");
848c2ecf20Sopenharmony_cisdw_slave_attr(clk_stop_timeout, "%d\n");
858c2ecf20Sopenharmony_cisdw_slave_attr(ch_prep_timeout, "%d\n");
868c2ecf20Sopenharmony_cisdw_slave_attr(reset_behave, "%d\n");
878c2ecf20Sopenharmony_cisdw_slave_attr(high_PHY_capable, "%d\n");
888c2ecf20Sopenharmony_cisdw_slave_attr(paging_support, "%d\n");
898c2ecf20Sopenharmony_cisdw_slave_attr(bank_delay_support, "%d\n");
908c2ecf20Sopenharmony_cisdw_slave_attr(p15_behave, "%d\n");
918c2ecf20Sopenharmony_cisdw_slave_attr(master_count, "%d\n");
928c2ecf20Sopenharmony_cisdw_slave_attr(source_ports, "0x%x\n");
938c2ecf20Sopenharmony_cisdw_slave_attr(sink_ports, "0x%x\n");
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev,
968c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return sdw_slave_modalias(slave, buf, 256);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct attribute *slave_attrs[] = {
1058c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
1068c2ecf20Sopenharmony_ci	NULL,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(slave);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct attribute *slave_dev_attrs[] = {
1118c2ecf20Sopenharmony_ci	&dev_attr_mipi_revision.attr,
1128c2ecf20Sopenharmony_ci	&dev_attr_wake_capable.attr,
1138c2ecf20Sopenharmony_ci	&dev_attr_test_mode_capable.attr,
1148c2ecf20Sopenharmony_ci	&dev_attr_clk_stop_mode1.attr,
1158c2ecf20Sopenharmony_ci	&dev_attr_simple_clk_stop_capable.attr,
1168c2ecf20Sopenharmony_ci	&dev_attr_clk_stop_timeout.attr,
1178c2ecf20Sopenharmony_ci	&dev_attr_ch_prep_timeout.attr,
1188c2ecf20Sopenharmony_ci	&dev_attr_reset_behave.attr,
1198c2ecf20Sopenharmony_ci	&dev_attr_high_PHY_capable.attr,
1208c2ecf20Sopenharmony_ci	&dev_attr_paging_support.attr,
1218c2ecf20Sopenharmony_ci	&dev_attr_bank_delay_support.attr,
1228c2ecf20Sopenharmony_ci	&dev_attr_p15_behave.attr,
1238c2ecf20Sopenharmony_ci	&dev_attr_master_count.attr,
1248c2ecf20Sopenharmony_ci	&dev_attr_source_ports.attr,
1258c2ecf20Sopenharmony_ci	&dev_attr_sink_ports.attr,
1268c2ecf20Sopenharmony_ci	NULL,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory
1318c2ecf20Sopenharmony_ci * for device-level properties
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic struct attribute_group sdw_slave_dev_attr_group = {
1348c2ecf20Sopenharmony_ci	.attrs	= slave_dev_attrs,
1358c2ecf20Sopenharmony_ci	.name = "dev-properties",
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*
1398c2ecf20Sopenharmony_ci * DP0 sysfs
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define sdw_dp0_attr(field, format_string)				\
1438c2ecf20Sopenharmony_cistatic ssize_t field##_show(struct device *dev,				\
1448c2ecf20Sopenharmony_ci			    struct device_attribute *attr,		\
1458c2ecf20Sopenharmony_ci			    char *buf)					\
1468c2ecf20Sopenharmony_ci{									\
1478c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);			\
1488c2ecf20Sopenharmony_ci	return sprintf(buf, format_string, slave->prop.dp0_prop->field);\
1498c2ecf20Sopenharmony_ci}									\
1508c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field)
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cisdw_dp0_attr(max_word, "%d\n");
1538c2ecf20Sopenharmony_cisdw_dp0_attr(min_word, "%d\n");
1548c2ecf20Sopenharmony_cisdw_dp0_attr(BRA_flow_controlled, "%d\n");
1558c2ecf20Sopenharmony_cisdw_dp0_attr(simple_ch_prep_sm, "%d\n");
1568c2ecf20Sopenharmony_cisdw_dp0_attr(imp_def_interrupts, "0x%x\n");
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic ssize_t words_show(struct device *dev,
1598c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);
1628c2ecf20Sopenharmony_ci	ssize_t size = 0;
1638c2ecf20Sopenharmony_ci	int i;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (i = 0; i < slave->prop.dp0_prop->num_words; i++)
1668c2ecf20Sopenharmony_ci		size += sprintf(buf + size, "%d ",
1678c2ecf20Sopenharmony_ci				slave->prop.dp0_prop->words[i]);
1688c2ecf20Sopenharmony_ci	size += sprintf(buf + size, "\n");
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return size;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(words);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct attribute *dp0_attrs[] = {
1758c2ecf20Sopenharmony_ci	&dev_attr_max_word.attr,
1768c2ecf20Sopenharmony_ci	&dev_attr_min_word.attr,
1778c2ecf20Sopenharmony_ci	&dev_attr_words.attr,
1788c2ecf20Sopenharmony_ci	&dev_attr_BRA_flow_controlled.attr,
1798c2ecf20Sopenharmony_ci	&dev_attr_simple_ch_prep_sm.attr,
1808c2ecf20Sopenharmony_ci	&dev_attr_imp_def_interrupts.attr,
1818c2ecf20Sopenharmony_ci	NULL,
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*
1858c2ecf20Sopenharmony_ci * we don't use ATTRIBUTES_GROUP here since we want to add a subdirectory
1868c2ecf20Sopenharmony_ci * for dp0-level properties
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_cistatic const struct attribute_group dp0_group = {
1898c2ecf20Sopenharmony_ci	.attrs = dp0_attrs,
1908c2ecf20Sopenharmony_ci	.name = "dp0",
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciint sdw_slave_sysfs_init(struct sdw_slave *slave)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = devm_device_add_groups(&slave->dev, slave_groups);
1988c2ecf20Sopenharmony_ci	if (ret < 0)
1998c2ecf20Sopenharmony_ci		return ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ret = devm_device_add_group(&slave->dev, &sdw_slave_dev_attr_group);
2028c2ecf20Sopenharmony_ci	if (ret < 0)
2038c2ecf20Sopenharmony_ci		return ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (slave->prop.dp0_prop) {
2068c2ecf20Sopenharmony_ci		ret = devm_device_add_group(&slave->dev, &dp0_group);
2078c2ecf20Sopenharmony_ci		if (ret < 0)
2088c2ecf20Sopenharmony_ci			return ret;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (slave->prop.source_ports || slave->prop.sink_ports) {
2128c2ecf20Sopenharmony_ci		ret = sdw_slave_sysfs_dpn_init(slave);
2138c2ecf20Sopenharmony_ci		if (ret < 0)
2148c2ecf20Sopenharmony_ci			return ret;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/*
2218c2ecf20Sopenharmony_ci * the status is shown in capital letters for UNATTACHED and RESERVED
2228c2ecf20Sopenharmony_ci * on purpose, to highligh users to the fact that these status values
2238c2ecf20Sopenharmony_ci * are not expected.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_cistatic const char *const slave_status[] = {
2268c2ecf20Sopenharmony_ci	[SDW_SLAVE_UNATTACHED] =  "UNATTACHED",
2278c2ecf20Sopenharmony_ci	[SDW_SLAVE_ATTACHED] = "Attached",
2288c2ecf20Sopenharmony_ci	[SDW_SLAVE_ALERT] = "Alert",
2298c2ecf20Sopenharmony_ci	[SDW_SLAVE_RESERVED] = "RESERVED",
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic ssize_t status_show(struct device *dev,
2338c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", slave_status[slave->status]);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(status);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic ssize_t device_number_show(struct device *dev,
2428c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct sdw_slave *slave = dev_to_sdw_dev(dev);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (slave->status == SDW_SLAVE_UNATTACHED)
2478c2ecf20Sopenharmony_ci		return sprintf(buf, "%s", "N/A");
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		return sprintf(buf, "%d", slave->dev_num);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device_number);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic struct attribute *slave_status_attrs[] = {
2548c2ecf20Sopenharmony_ci	&dev_attr_status.attr,
2558c2ecf20Sopenharmony_ci	&dev_attr_device_number.attr,
2568c2ecf20Sopenharmony_ci	NULL,
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/*
2608c2ecf20Sopenharmony_ci * we don't use ATTRIBUTES_GROUP here since the group is used in a
2618c2ecf20Sopenharmony_ci * separate file and can't be handled as a static.
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic const struct attribute_group sdw_slave_status_attr_group = {
2648c2ecf20Sopenharmony_ci	.attrs	= slave_status_attrs,
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciconst struct attribute_group *sdw_slave_status_attr_groups[] = {
2688c2ecf20Sopenharmony_ci	&sdw_slave_status_attr_group,
2698c2ecf20Sopenharmony_ci	NULL
2708c2ecf20Sopenharmony_ci};
271