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