xref: /linux/drivers/reset/amlogic/reset-meson-aux.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Amlogic Meson Reset Auxiliary driver
4  *
5  * Copyright (c) 2024 BayLibre, SAS.
6  * Author: Jerome Brunet <jbrunet@baylibre.com>
7  */
8 
9 #include <linux/err.h>
10 #include <linux/module.h>
11 #include <linux/auxiliary_bus.h>
12 #include <linux/regmap.h>
13 #include <linux/reset-controller.h>
14 #include <linux/slab.h>
15 
16 #include "reset-meson.h"
17 #include <soc/amlogic/reset-meson-aux.h>
18 
19 static DEFINE_IDA(meson_rst_aux_ida);
20 
21 struct meson_reset_adev {
22 	struct auxiliary_device adev;
23 	struct regmap *map;
24 };
25 
26 #define to_meson_reset_adev(_adev) \
27 	container_of((_adev), struct meson_reset_adev, adev)
28 
29 static const struct meson_reset_param meson_g12a_audio_param = {
30 	.reset_ops	= &meson_reset_toggle_ops,
31 	.reset_num	= 26,
32 	.level_offset	= 0x24,
33 };
34 
35 static const struct meson_reset_param meson_sm1_audio_param = {
36 	.reset_ops	= &meson_reset_toggle_ops,
37 	.reset_num	= 39,
38 	.level_offset	= 0x28,
39 };
40 
41 static const struct auxiliary_device_id meson_reset_aux_ids[] = {
42 	{
43 		.name = "axg-audio-clkc.rst-g12a",
44 		.driver_data = (kernel_ulong_t)&meson_g12a_audio_param,
45 	}, {
46 		.name = "axg-audio-clkc.rst-sm1",
47 		.driver_data = (kernel_ulong_t)&meson_sm1_audio_param,
48 	}, {}
49 };
50 MODULE_DEVICE_TABLE(auxiliary, meson_reset_aux_ids);
51 
52 static int meson_reset_aux_probe(struct auxiliary_device *adev,
53 				 const struct auxiliary_device_id *id)
54 {
55 	const struct meson_reset_param *param =
56 		(const struct meson_reset_param *)(id->driver_data);
57 	struct meson_reset_adev *raux =
58 		to_meson_reset_adev(adev);
59 
60 	return meson_reset_controller_register(&adev->dev, raux->map, param);
61 }
62 
63 static struct auxiliary_driver meson_reset_aux_driver = {
64 	.probe		= meson_reset_aux_probe,
65 	.id_table	= meson_reset_aux_ids,
66 };
67 module_auxiliary_driver(meson_reset_aux_driver);
68 
69 static void meson_rst_aux_release(struct device *dev)
70 {
71 	struct auxiliary_device *adev = to_auxiliary_dev(dev);
72 	struct meson_reset_adev *raux =
73 		to_meson_reset_adev(adev);
74 
75 	ida_free(&meson_rst_aux_ida, adev->id);
76 	kfree(raux);
77 }
78 
79 static void meson_rst_aux_unregister_adev(void *_adev)
80 {
81 	struct auxiliary_device *adev = _adev;
82 
83 	auxiliary_device_delete(adev);
84 	auxiliary_device_uninit(adev);
85 }
86 
87 int devm_meson_rst_aux_register(struct device *dev,
88 				struct regmap *map,
89 				const char *adev_name)
90 {
91 	struct meson_reset_adev *raux;
92 	struct auxiliary_device *adev;
93 	int ret;
94 
95 	raux = kzalloc(sizeof(*raux), GFP_KERNEL);
96 	if (!raux)
97 		return -ENOMEM;
98 
99 	ret = ida_alloc(&meson_rst_aux_ida, GFP_KERNEL);
100 	if (ret < 0)
101 		goto raux_free;
102 
103 	raux->map = map;
104 
105 	adev = &raux->adev;
106 	adev->id = ret;
107 	adev->name = adev_name;
108 	adev->dev.parent = dev;
109 	adev->dev.release = meson_rst_aux_release;
110 	device_set_of_node_from_dev(&adev->dev, dev);
111 
112 	ret = auxiliary_device_init(adev);
113 	if (ret)
114 		goto ida_free;
115 
116 	ret = __auxiliary_device_add(adev, dev->driver->name);
117 	if (ret) {
118 		auxiliary_device_uninit(adev);
119 		return ret;
120 	}
121 
122 	return devm_add_action_or_reset(dev, meson_rst_aux_unregister_adev,
123 					adev);
124 
125 ida_free:
126 	ida_free(&meson_rst_aux_ida, adev->id);
127 raux_free:
128 	kfree(raux);
129 	return ret;
130 }
131 EXPORT_SYMBOL_GPL(devm_meson_rst_aux_register);
132 
133 MODULE_DESCRIPTION("Amlogic Meson Reset Auxiliary driver");
134 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
135 MODULE_LICENSE("Dual BSD/GPL");
136 MODULE_IMPORT_NS(MESON_RESET);
137