File Index
Files and directories
Sign in
FILE

soc-ep93xx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/soc/cirrus/soc-ep93xx.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * SoC driver for Cirrus EP93xx chips.
  4 * Copyright (C) 2022 Nikita Shubin <nikita.shubin@maquefel.me>
  5 *
  6 * Based on a rewrite of arch/arm/mach-ep93xx/core.c
  7 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
  8 * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
  9 *
 10 * Thanks go to Michael Burian and Ray Lehtiniemi for their key
 11 * role in the ep93xx Linux community.
 12 */
 13
 14#include <linux/bits.h>
 15#include <linux/cleanup.h>
 16#include <linux/init.h>
 17#include <linux/mfd/syscon.h>
 18#include <linux/of.h>
 19#include <linux/of_fdt.h>
 20#include <linux/platform_device.h>
 21#include <linux/regmap.h>
 22#include <linux/slab.h>
 23#include <linux/spinlock.h>
 24#include <linux/sys_soc.h>
 25
 26#include <linux/soc/cirrus/ep93xx.h>
 27
 28#define EP93XX_SYSCON_DEVCFG		0x80
 29
 30#define EP93XX_SWLOCK_MAGICK		0xaa
 31#define EP93XX_SYSCON_SWLOCK		0xc0
 32#define EP93XX_SYSCON_SYSCFG		0x9c
 33#define EP93XX_SYSCON_SYSCFG_REV_MASK	GENMASK(31, 28)
 34#define EP93XX_SYSCON_SYSCFG_REV_SHIFT	28
 35
 36struct ep93xx_map_info {
 37	spinlock_t lock;
 38	void __iomem *base;
 39	struct regmap *map;
 40};
 41
 42/*
 43 * EP93xx System Controller software locked register write
 44 *
 45 * Logic safeguards are included to condition the control signals for
 46 * power connection to the matrix to prevent part damage. In addition, a
 47 * software lock register is included that must be written with 0xAA
 48 * before each register write to change the values of the four switch
 49 * matrix control registers.
 50 */
 51static void ep93xx_regmap_write(struct regmap *map, spinlock_t *lock,
 52				 unsigned int reg, unsigned int val)
 53{
 54	guard(spinlock_irqsave)(lock);
 55
 56	regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
 57	regmap_write(map, reg, val);
 58}
 59
 60static void ep93xx_regmap_update_bits(struct regmap *map, spinlock_t *lock,
 61				      unsigned int reg, unsigned int mask,
 62				      unsigned int val)
 63{
 64	guard(spinlock_irqsave)(lock);
 65
 66	regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
 67	/* force write is required to clear swlock if no changes are made */
 68	regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
 69}
 70
 71static void ep93xx_unregister_adev(void *_adev)
 72{
 73	struct auxiliary_device *adev = _adev;
 74
 75	auxiliary_device_delete(adev);
 76	auxiliary_device_uninit(adev);
 77}
 78
 79static void ep93xx_adev_release(struct device *dev)
 80{
 81	struct auxiliary_device *adev = to_auxiliary_dev(dev);
 82	struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev);
 83
 84	kfree(rdev);
 85}
 86
 87static struct auxiliary_device __init *ep93xx_adev_alloc(struct device *parent,
 88							 const char *name,
 89							 struct ep93xx_map_info *info)
 90{
 91	struct ep93xx_regmap_adev *rdev __free(kfree) = NULL;
 92	struct auxiliary_device *adev;
 93	int ret;
 94
 95	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
 96	if (!rdev)
 97		return ERR_PTR(-ENOMEM);
 98
 99	rdev->map = info->map;
100	rdev->base = info->base;
101	rdev->lock = &info->lock;
102	rdev->write = ep93xx_regmap_write;
103	rdev->update_bits = ep93xx_regmap_update_bits;
104
105	adev = &rdev->adev;
106	adev->name = name;
107	adev->dev.parent = parent;
108	adev->dev.release = ep93xx_adev_release;
109
110	ret = auxiliary_device_init(adev);
111	if (ret)
112		return ERR_PTR(ret);
113
114	return &no_free_ptr(rdev)->adev;
115}
116
117static int __init ep93xx_controller_register(struct device *parent, const char *name,
118					     struct ep93xx_map_info *info)
119{
120	struct auxiliary_device *adev;
121	int ret;
122
123	adev = ep93xx_adev_alloc(parent, name, info);
124	if (IS_ERR(adev))
125		return PTR_ERR(adev);
126
127	ret = auxiliary_device_add(adev);
128	if (ret) {
129		auxiliary_device_uninit(adev);
130		return ret;
131	}
132
133	return devm_add_action_or_reset(parent, ep93xx_unregister_adev, adev);
134}
135
136static unsigned int __init ep93xx_soc_revision(struct regmap *map)
137{
138	unsigned int val;
139
140	regmap_read(map, EP93XX_SYSCON_SYSCFG, &val);
141	val &= EP93XX_SYSCON_SYSCFG_REV_MASK;
142	val >>= EP93XX_SYSCON_SYSCFG_REV_SHIFT;
143	return val;
144}
145
146static const char __init *ep93xx_get_soc_rev(unsigned int rev)
147{
148	switch (rev) {
149	case EP93XX_CHIP_REV_D0:
150		return "D0";
151	case EP93XX_CHIP_REV_D1:
152		return "D1";
153	case EP93XX_CHIP_REV_E0:
154		return "E0";
155	case EP93XX_CHIP_REV_E1:
156		return "E1";
157	case EP93XX_CHIP_REV_E2:
158		return "E2";
159	default:
160		return "unknown";
161	}
162}
163
164static const char *pinctrl_names[] __initconst = {
165	"pinctrl-ep9301",	/* EP93XX_9301_SOC */
166	"pinctrl-ep9307",	/* EP93XX_9307_SOC */
167	"pinctrl-ep9312",	/* EP93XX_9312_SOC */
168};
169
170static int __init ep93xx_syscon_probe(struct platform_device *pdev)
171{
172	enum ep93xx_soc_model model;
173	struct ep93xx_map_info *map_info;
174	struct soc_device_attribute *attrs;
175	struct soc_device *soc_dev;
176	struct device *dev = &pdev->dev;
177	struct regmap *map;
178	void __iomem *base;
179	unsigned int rev;
180	int ret;
181
182	model = (enum ep93xx_soc_model)(uintptr_t)device_get_match_data(dev);
183
184	map = device_node_to_regmap(dev->of_node);
185	if (IS_ERR(map))
186		return PTR_ERR(map);
187
188	base = devm_platform_ioremap_resource(pdev, 0);
189	if (IS_ERR(base))
190		return PTR_ERR(base);
191
192	attrs = devm_kzalloc(dev, sizeof(*attrs), GFP_KERNEL);
193	if (!attrs)
194		return -ENOMEM;
195
196	rev = ep93xx_soc_revision(map);
197
198	attrs->machine = of_flat_dt_get_machine_name();
199	attrs->family = "Cirrus Logic EP93xx";
200	attrs->revision = ep93xx_get_soc_rev(rev);
201
202	soc_dev = soc_device_register(attrs);
203	if (IS_ERR(soc_dev))
204		return PTR_ERR(soc_dev);
205
206	map_info = devm_kzalloc(dev, sizeof(*map_info), GFP_KERNEL);
207	if (!map_info)
208		return -ENOMEM;
209
210	spin_lock_init(&map_info->lock);
211	map_info->map = map;
212	map_info->base = base;
213
214	ret = ep93xx_controller_register(dev, pinctrl_names[model], map_info);
215	if (ret)
216		dev_err(dev, "registering pinctrl controller failed\n");
217
218	/*
219	 * EP93xx SSP clock rate was doubled in version E2. For more information
220	 * see section 6 "2x SSP (Synchronous Serial Port) Clock – Revision E2 only":
221	 *     http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
222	 */
223	if (rev == EP93XX_CHIP_REV_E2)
224		ret = ep93xx_controller_register(dev, "clk-ep93xx.e2", map_info);
225	else
226		ret = ep93xx_controller_register(dev, "clk-ep93xx", map_info);
227	if (ret)
228		dev_err(dev, "registering clock controller failed\n");
229
230	ret = ep93xx_controller_register(dev, "reset-ep93xx", map_info);
231	if (ret)
232		dev_err(dev, "registering reset controller failed\n");
233
234	return 0;
235}
236
237static const struct of_device_id ep9301_syscon_of_device_ids[] = {
238	{ .compatible	= "cirrus,ep9301-syscon", .data = (void *)EP93XX_9301_SOC },
239	{ .compatible	= "cirrus,ep9302-syscon", .data = (void *)EP93XX_9301_SOC },
240	{ .compatible	= "cirrus,ep9307-syscon", .data = (void *)EP93XX_9307_SOC },
241	{ .compatible	= "cirrus,ep9312-syscon", .data = (void *)EP93XX_9312_SOC },
242	{ .compatible	= "cirrus,ep9315-syscon", .data = (void *)EP93XX_9312_SOC },
243	{ /* sentinel */ }
244};
245
246static struct platform_driver ep9301_syscon_driver = {
247	.driver = {
248		.name = "ep9301-syscon",
249		.of_match_table = ep9301_syscon_of_device_ids,
250	},
251};
252builtin_platform_driver_probe(ep9301_syscon_driver, ep93xx_syscon_probe);
~/ Read-only file preview.