brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 3e79b3b Raw
253 lines · 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.c7 * 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 key11 * 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		0x8029 30#define EP93XX_SWLOCK_MAGICK		0xaa31#define EP93XX_SYSCON_SWLOCK		0xc032#define EP93XX_SYSCON_SYSCFG		0x9c33#define EP93XX_SYSCON_SYSCFG_REV_MASK	GENMASK(31, 28)34#define EP93XX_SYSCON_SYSCFG_REV_SHIFT	2835 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 write44 *45 * Logic safeguards are included to condition the control signals for46 * power connection to the matrix to prevent part damage. In addition, a47 * software lock register is included that must be written with 0xAA48 * before each register write to change the values of the four switch49 * 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 information220	 * see section 6 "2x SSP (Synchronous Serial Port) Clock – Revision E2 only":221	 *     http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf222	 */223	if (rev == EP93XX_CHIP_REV_E2)224		ret = ep93xx_controller_register(dev, "clk-ep93xx.e2", map_info);225	else226		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);253