1  // SPDX-License-Identifier: GPL-2.0-only
2  // Copyright(c) 2019-2020 Intel Corporation.
3  
4  #include <linux/device.h>
5  #include <linux/acpi.h>
6  #include <linux/pm_runtime.h>
7  #include <linux/soundwire/sdw.h>
8  #include <linux/soundwire/sdw_type.h>
9  #include "bus.h"
10  
11  /*
12   * The 3s value for autosuspend will only be used if there are no
13   * devices physically attached on a bus segment. In practice enabling
14   * the bus operation will result in children devices become active and
15   * the master device will only suspend when all its children are no
16   * longer active.
17   */
18  #define SDW_MASTER_SUSPEND_DELAY_MS 3000
19  
20  /*
21   * The sysfs for properties reflects the MIPI description as given
22   * in the MIPI DisCo spec
23   *
24   * Base file is:
25   *	sdw-master-N
26   *      |---- revision
27   *      |---- clk_stop_modes
28   *      |---- max_clk_freq
29   *      |---- clk_freq
30   *      |---- clk_gears
31   *      |---- default_row
32   *      |---- default_col
33   *      |---- dynamic_shape
34   *      |---- err_threshold
35   */
36  
37  #define sdw_master_attr(field, format_string)				\
38  static ssize_t field##_show(struct device *dev,				\
39  			    struct device_attribute *attr,		\
40  			    char *buf)					\
41  {									\
42  	struct sdw_master_device *md = dev_to_sdw_master_device(dev);	\
43  	return sprintf(buf, format_string, md->bus->prop.field);	\
44  }									\
45  static DEVICE_ATTR_RO(field)
46  
47  sdw_master_attr(revision, "0x%x\n");
48  sdw_master_attr(clk_stop_modes, "0x%x\n");
49  sdw_master_attr(max_clk_freq, "%d\n");
50  sdw_master_attr(default_row, "%d\n");
51  sdw_master_attr(default_col, "%d\n");
52  sdw_master_attr(default_frame_rate, "%d\n");
53  sdw_master_attr(dynamic_frame, "%d\n");
54  sdw_master_attr(err_threshold, "%d\n");
55  
clock_frequencies_show(struct device * dev,struct device_attribute * attr,char * buf)56  static ssize_t clock_frequencies_show(struct device *dev,
57  				      struct device_attribute *attr, char *buf)
58  {
59  	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
60  	ssize_t size = 0;
61  	int i;
62  
63  	for (i = 0; i < md->bus->prop.num_clk_freq; i++)
64  		size += sprintf(buf + size, "%8d ",
65  				md->bus->prop.clk_freq[i]);
66  	size += sprintf(buf + size, "\n");
67  
68  	return size;
69  }
70  static DEVICE_ATTR_RO(clock_frequencies);
71  
clock_gears_show(struct device * dev,struct device_attribute * attr,char * buf)72  static ssize_t clock_gears_show(struct device *dev,
73  				struct device_attribute *attr, char *buf)
74  {
75  	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
76  	ssize_t size = 0;
77  	int i;
78  
79  	for (i = 0; i < md->bus->prop.num_clk_gears; i++)
80  		size += sprintf(buf + size, "%8d ",
81  				md->bus->prop.clk_gears[i]);
82  	size += sprintf(buf + size, "\n");
83  
84  	return size;
85  }
86  static DEVICE_ATTR_RO(clock_gears);
87  
88  static struct attribute *master_node_attrs[] = {
89  	&dev_attr_revision.attr,
90  	&dev_attr_clk_stop_modes.attr,
91  	&dev_attr_max_clk_freq.attr,
92  	&dev_attr_default_row.attr,
93  	&dev_attr_default_col.attr,
94  	&dev_attr_default_frame_rate.attr,
95  	&dev_attr_dynamic_frame.attr,
96  	&dev_attr_err_threshold.attr,
97  	&dev_attr_clock_frequencies.attr,
98  	&dev_attr_clock_gears.attr,
99  	NULL,
100  };
101  ATTRIBUTE_GROUPS(master_node);
102  
sdw_master_device_release(struct device * dev)103  static void sdw_master_device_release(struct device *dev)
104  {
105  	struct sdw_master_device *md = dev_to_sdw_master_device(dev);
106  
107  	kfree(md);
108  }
109  
110  static const struct dev_pm_ops master_dev_pm = {
111  	SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
112  			   pm_generic_runtime_resume, NULL)
113  };
114  
115  const struct device_type sdw_master_type = {
116  	.name =		"soundwire_master",
117  	.release =	sdw_master_device_release,
118  	.pm = &master_dev_pm,
119  };
120  
121  /**
122   * sdw_master_device_add() - create a Linux Master Device representation.
123   * @bus: SDW bus instance
124   * @parent: parent device
125   * @fwnode: firmware node handle
126   */
sdw_master_device_add(struct sdw_bus * bus,struct device * parent,struct fwnode_handle * fwnode)127  int sdw_master_device_add(struct sdw_bus *bus, struct device *parent,
128  			  struct fwnode_handle *fwnode)
129  {
130  	struct sdw_master_device *md;
131  	int ret;
132  
133  	if (!parent)
134  		return -EINVAL;
135  
136  	md = kzalloc(sizeof(*md), GFP_KERNEL);
137  	if (!md)
138  		return -ENOMEM;
139  
140  	md->dev.bus = &sdw_bus_type;
141  	md->dev.type = &sdw_master_type;
142  	md->dev.parent = parent;
143  	md->dev.groups = master_node_groups;
144  	md->dev.of_node = parent->of_node;
145  	md->dev.fwnode = fwnode;
146  	md->dev.dma_mask = parent->dma_mask;
147  
148  	dev_set_name(&md->dev, "sdw-master-%d-%d", bus->controller_id, bus->link_id);
149  
150  	ret = device_register(&md->dev);
151  	if (ret) {
152  		dev_err(parent, "Failed to add master: ret %d\n", ret);
153  		/*
154  		 * On err, don't free but drop ref as this will be freed
155  		 * when release method is invoked.
156  		 */
157  		put_device(&md->dev);
158  		goto device_register_err;
159  	}
160  
161  	/* add shortcuts to improve code readability/compactness */
162  	md->bus = bus;
163  	bus->dev = &md->dev;
164  	bus->md = md;
165  
166  	pm_runtime_set_autosuspend_delay(&bus->md->dev, SDW_MASTER_SUSPEND_DELAY_MS);
167  	pm_runtime_use_autosuspend(&bus->md->dev);
168  	pm_runtime_mark_last_busy(&bus->md->dev);
169  	pm_runtime_set_active(&bus->md->dev);
170  	pm_runtime_enable(&bus->md->dev);
171  	pm_runtime_idle(&bus->md->dev);
172  device_register_err:
173  	return ret;
174  }
175  
176  /**
177   * sdw_master_device_del() - delete a Linux Master Device representation.
178   * @bus: bus handle
179   *
180   * This function is the dual of sdw_master_device_add()
181   */
sdw_master_device_del(struct sdw_bus * bus)182  int sdw_master_device_del(struct sdw_bus *bus)
183  {
184  	pm_runtime_disable(&bus->md->dev);
185  	device_unregister(bus->dev);
186  
187  	return 0;
188  }
189