| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * SoC driver for Cirrus EP93xx chips.
- * Copyright (C) 2022 Nikita Shubin <nikita.shubin@maquefel.me>
- *
- * Based on a rewrite of arch/arm/mach-ep93xx/core.c
- * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
- * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
- *
- * Thanks go to Michael Burian and Ray Lehtiniemi for their key
- * role in the ep93xx Linux community.
- */
- #include <linux/bits.h>
- #include <linux/cleanup.h>
- #include <linux/init.h>
- #include <linux/mfd/syscon.h>
- #include <linux/of.h>
- #include <linux/of_fdt.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <linux/sys_soc.h>
- #include <linux/soc/cirrus/ep93xx.h>
- #define EP93XX_SYSCON_DEVCFG 0x80
- #define EP93XX_SWLOCK_MAGICK 0xaa
- #define EP93XX_SYSCON_SWLOCK 0xc0
- #define EP93XX_SYSCON_SYSCFG 0x9c
- #define EP93XX_SYSCON_SYSCFG_REV_MASK GENMASK(31, 28)
- #define EP93XX_SYSCON_SYSCFG_REV_SHIFT 28
- struct ep93xx_map_info {
- spinlock_t lock;
- void __iomem *base;
- struct regmap *map;
- };
- /*
- * EP93xx System Controller software locked register write
- *
- * Logic safeguards are included to condition the control signals for
- * power connection to the matrix to prevent part damage. In addition, a
- * software lock register is included that must be written with 0xAA
- * before each register write to change the values of the four switch
- * matrix control registers.
- */
- static void ep93xx_regmap_write(struct regmap *map, spinlock_t *lock,
- unsigned int reg, unsigned int val)
- {
- guard(spinlock_irqsave)(lock);
- regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
- regmap_write(map, reg, val);
- }
- static void ep93xx_regmap_update_bits(struct regmap *map, spinlock_t *lock,
- unsigned int reg, unsigned int mask,
- unsigned int val)
- {
- guard(spinlock_irqsave)(lock);
- regmap_write(map, EP93XX_SYSCON_SWLOCK, EP93XX_SWLOCK_MAGICK);
- /* force write is required to clear swlock if no changes are made */
- regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
- }
- static void ep93xx_unregister_adev(void *_adev)
- {
- struct auxiliary_device *adev = _adev;
- auxiliary_device_delete(adev);
- auxiliary_device_uninit(adev);
- }
- static void ep93xx_adev_release(struct device *dev)
- {
- struct auxiliary_device *adev = to_auxiliary_dev(dev);
- struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev);
- kfree(rdev);
- }
- static struct auxiliary_device __init *ep93xx_adev_alloc(struct device *parent,
- const char *name,
- struct ep93xx_map_info *info)
- {
- struct ep93xx_regmap_adev *rdev __free(kfree) = NULL;
- struct auxiliary_device *adev;
- int ret;
- rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
- if (!rdev)
- return ERR_PTR(-ENOMEM);
- rdev->map = info->map;
- rdev->base = info->base;
- rdev->lock = &info->lock;
- rdev->write = ep93xx_regmap_write;
- rdev->update_bits = ep93xx_regmap_update_bits;
- adev = &rdev->adev;
- adev->name = name;
- adev->dev.parent = parent;
- adev->dev.release = ep93xx_adev_release;
- ret = auxiliary_device_init(adev);
- if (ret)
- return ERR_PTR(ret);
- return &no_free_ptr(rdev)->adev;
- }
- static int __init ep93xx_controller_register(struct device *parent, const char *name,
- struct ep93xx_map_info *info)
- {
- struct auxiliary_device *adev;
- int ret;
- adev = ep93xx_adev_alloc(parent, name, info);
- if (IS_ERR(adev))
- return PTR_ERR(adev);
- ret = auxiliary_device_add(adev);
- if (ret) {
- auxiliary_device_uninit(adev);
- return ret;
- }
- return devm_add_action_or_reset(parent, ep93xx_unregister_adev, adev);
- }
- static unsigned int __init ep93xx_soc_revision(struct regmap *map)
- {
- unsigned int val;
- regmap_read(map, EP93XX_SYSCON_SYSCFG, &val);
- val &= EP93XX_SYSCON_SYSCFG_REV_MASK;
- val >>= EP93XX_SYSCON_SYSCFG_REV_SHIFT;
- return val;
- }
- static const char __init *ep93xx_get_soc_rev(unsigned int rev)
- {
- switch (rev) {
- case EP93XX_CHIP_REV_D0:
- return "D0";
- case EP93XX_CHIP_REV_D1:
- return "D1";
- case EP93XX_CHIP_REV_E0:
- return "E0";
- case EP93XX_CHIP_REV_E1:
- return "E1";
- case EP93XX_CHIP_REV_E2:
- return "E2";
- default:
- return "unknown";
- }
- }
- static const char *pinctrl_names[] __initconst = {
- "pinctrl-ep9301", /* EP93XX_9301_SOC */
- "pinctrl-ep9307", /* EP93XX_9307_SOC */
- "pinctrl-ep9312", /* EP93XX_9312_SOC */
- };
- static int __init ep93xx_syscon_probe(struct platform_device *pdev)
- {
- enum ep93xx_soc_model model;
- struct ep93xx_map_info *map_info;
- struct soc_device_attribute *attrs;
- struct soc_device *soc_dev;
- struct device *dev = &pdev->dev;
- struct regmap *map;
- void __iomem *base;
- unsigned int rev;
- int ret;
- model = (enum ep93xx_soc_model)(uintptr_t)device_get_match_data(dev);
- map = device_node_to_regmap(dev->of_node);
- if (IS_ERR(map))
- return PTR_ERR(map);
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
- attrs = devm_kzalloc(dev, sizeof(*attrs), GFP_KERNEL);
- if (!attrs)
- return -ENOMEM;
- rev = ep93xx_soc_revision(map);
- attrs->machine = of_flat_dt_get_machine_name();
- attrs->family = "Cirrus Logic EP93xx";
- attrs->revision = ep93xx_get_soc_rev(rev);
- soc_dev = soc_device_register(attrs);
- if (IS_ERR(soc_dev))
- return PTR_ERR(soc_dev);
- map_info = devm_kzalloc(dev, sizeof(*map_info), GFP_KERNEL);
- if (!map_info)
- return -ENOMEM;
- spin_lock_init(&map_info->lock);
- map_info->map = map;
- map_info->base = base;
- ret = ep93xx_controller_register(dev, pinctrl_names[model], map_info);
- if (ret)
- dev_err(dev, "registering pinctrl controller failed\n");
- /*
- * EP93xx SSP clock rate was doubled in version E2. For more information
- * see section 6 "2x SSP (Synchronous Serial Port) Clock – Revision E2 only":
- * http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
- */
- if (rev == EP93XX_CHIP_REV_E2)
- ret = ep93xx_controller_register(dev, "clk-ep93xx.e2", map_info);
- else
- ret = ep93xx_controller_register(dev, "clk-ep93xx", map_info);
- if (ret)
- dev_err(dev, "registering clock controller failed\n");
- ret = ep93xx_controller_register(dev, "reset-ep93xx", map_info);
- if (ret)
- dev_err(dev, "registering reset controller failed\n");
- return 0;
- }
- static const struct of_device_id ep9301_syscon_of_device_ids[] = {
- { .compatible = "cirrus,ep9301-syscon", .data = (void *)EP93XX_9301_SOC },
- { .compatible = "cirrus,ep9302-syscon", .data = (void *)EP93XX_9301_SOC },
- { .compatible = "cirrus,ep9307-syscon", .data = (void *)EP93XX_9307_SOC },
- { .compatible = "cirrus,ep9312-syscon", .data = (void *)EP93XX_9312_SOC },
- { .compatible = "cirrus,ep9315-syscon", .data = (void *)EP93XX_9312_SOC },
- { /* sentinel */ }
- };
- static struct platform_driver ep9301_syscon_driver = {
- .driver = {
- .name = "ep9301-syscon",
- .of_match_table = ep9301_syscon_of_device_ids,
- },
- };
- builtin_platform_driver_probe(ep9301_syscon_driver, ep93xx_syscon_probe);
|