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
meson_reset_aux_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)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
meson_rst_aux_release(struct device * dev)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
meson_rst_aux_unregister_adev(void * _adev)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
devm_meson_rst_aux_register(struct device * dev,struct regmap * map,const char * adev_name)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