Skip to content

Commit 6eab0ce

Browse files
maquefelarndb
authored andcommitted
soc: Add SoC driver for Cirrus ep93xx
Add an SoC driver for the ep93xx. Currently there is only one thing not fitting into any other framework, and that is the swlock setting. Used for clock settings, pinctrl and restart. Signed-off-by: Nikita Shubin <[email protected]> Tested-by: Alexander Sverdlin <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Acked-by: Alexander Sverdlin <[email protected]> Acked-by: Vinod Koul <[email protected]> Signed-off-by: Arnd Bergmann <[email protected]>
1 parent eeb3dd5 commit 6eab0ce

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed

drivers/soc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ source "drivers/soc/aspeed/Kconfig"
77
source "drivers/soc/atmel/Kconfig"
88
source "drivers/soc/bcm/Kconfig"
99
source "drivers/soc/canaan/Kconfig"
10+
source "drivers/soc/cirrus/Kconfig"
1011
source "drivers/soc/fsl/Kconfig"
1112
source "drivers/soc/fujitsu/Kconfig"
1213
source "drivers/soc/hisilicon/Kconfig"

drivers/soc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ obj-y += aspeed/
88
obj-$(CONFIG_ARCH_AT91) += atmel/
99
obj-y += bcm/
1010
obj-$(CONFIG_ARCH_CANAAN) += canaan/
11+
obj-$(CONFIG_EP93XX_SOC) += cirrus/
1112
obj-$(CONFIG_ARCH_DOVE) += dove/
1213
obj-$(CONFIG_MACH_DOVE) += dove/
1314
obj-y += fsl/

drivers/soc/cirrus/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
3+
if ARCH_EP93XX
4+
5+
config EP93XX_SOC
6+
bool "Cirrus EP93xx chips SoC"
7+
select SOC_BUS
8+
select AUXILIARY_BUS
9+
default y if !EP93XX_SOC_COMMON
10+
help
11+
Enable support SoC for Cirrus EP93xx chips.
12+
13+
Cirrus EP93xx chips have several swlocked registers,
14+
this driver provides locked access for reset, pinctrl
15+
and clk devices implemented as auxiliary devices.
16+
17+
endif

drivers/soc/cirrus/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
obj-y += soc-ep93xx.o

drivers/soc/cirrus/soc-ep93xx.c

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* SoC driver for Cirrus EP93xx chips.
4+
* Copyright (C) 2022 Nikita Shubin <[email protected]>
5+
*
6+
* Based on a rewrite of arch/arm/mach-ep93xx/core.c
7+
* Copyright (C) 2006 Lennert Buytenhek <[email protected]>
8+
* Copyright (C) 2007 Herbert Valerio Riedel <[email protected]>
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+
36+
struct 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+
*/
51+
static 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+
60+
static 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+
71+
static 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+
79+
static 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+
87+
static 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+
117+
static 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+
136+
static 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+
146+
static 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+
164+
static 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+
170+
static 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+
237+
static 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+
246+
static struct platform_driver ep9301_syscon_driver = {
247+
.driver = {
248+
.name = "ep9301-syscon",
249+
.of_match_table = ep9301_syscon_of_device_ids,
250+
},
251+
};
252+
builtin_platform_driver_probe(ep9301_syscon_driver, ep93xx_syscon_probe);

0 commit comments

Comments
 (0)