1c1aca558SClaudiu Beznea // SPDX-License-Identifier: GPL-2.0 2c1aca558SClaudiu Beznea /* 3c1aca558SClaudiu Beznea * RZ System controller driver 4c1aca558SClaudiu Beznea * 5c1aca558SClaudiu Beznea * Copyright (C) 2024 Renesas Electronics Corp. 6c1aca558SClaudiu Beznea */ 7c1aca558SClaudiu Beznea 8c1aca558SClaudiu Beznea #include <linux/io.h> 9c1aca558SClaudiu Beznea #include <linux/of.h> 10c1aca558SClaudiu Beznea #include <linux/platform_device.h> 11c1aca558SClaudiu Beznea #include <linux/sys_soc.h> 12c1aca558SClaudiu Beznea 13c1aca558SClaudiu Beznea #include "rz-sysc.h" 14c1aca558SClaudiu Beznea 15c1aca558SClaudiu Beznea #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) 16c1aca558SClaudiu Beznea 17c1aca558SClaudiu Beznea /** 18c1aca558SClaudiu Beznea * struct rz_sysc - RZ SYSC private data structure 19c1aca558SClaudiu Beznea * @base: SYSC base address 20c1aca558SClaudiu Beznea * @dev: SYSC device pointer 21c1aca558SClaudiu Beznea */ 22c1aca558SClaudiu Beznea struct rz_sysc { 23c1aca558SClaudiu Beznea void __iomem *base; 24c1aca558SClaudiu Beznea struct device *dev; 25c1aca558SClaudiu Beznea }; 26c1aca558SClaudiu Beznea 27c1aca558SClaudiu Beznea static int rz_sysc_soc_init(struct rz_sysc *sysc, const struct of_device_id *match) 28c1aca558SClaudiu Beznea { 29c1aca558SClaudiu Beznea const struct rz_sysc_init_data *sysc_data = match->data; 30c1aca558SClaudiu Beznea const struct rz_sysc_soc_id_init_data *soc_data = sysc_data->soc_id_init_data; 31c1aca558SClaudiu Beznea struct soc_device_attribute *soc_dev_attr; 32c1aca558SClaudiu Beznea const char *soc_id_start, *soc_id_end; 33c1aca558SClaudiu Beznea u32 val, revision, specific_id; 34c1aca558SClaudiu Beznea struct soc_device *soc_dev; 35c1aca558SClaudiu Beznea char soc_id[32] = {0}; 36c1aca558SClaudiu Beznea size_t size; 37c1aca558SClaudiu Beznea 38c1aca558SClaudiu Beznea soc_id_start = strchr(match->compatible, ',') + 1; 39c1aca558SClaudiu Beznea soc_id_end = strchr(match->compatible, '-'); 40c1aca558SClaudiu Beznea size = soc_id_end - soc_id_start + 1; 41c1aca558SClaudiu Beznea if (size > 32) 42c1aca558SClaudiu Beznea size = sizeof(soc_id); 43c1aca558SClaudiu Beznea strscpy(soc_id, soc_id_start, size); 44c1aca558SClaudiu Beznea 45c1aca558SClaudiu Beznea soc_dev_attr = devm_kzalloc(sysc->dev, sizeof(*soc_dev_attr), GFP_KERNEL); 46c1aca558SClaudiu Beznea if (!soc_dev_attr) 47c1aca558SClaudiu Beznea return -ENOMEM; 48c1aca558SClaudiu Beznea 49c1aca558SClaudiu Beznea soc_dev_attr->family = devm_kstrdup(sysc->dev, soc_data->family, GFP_KERNEL); 50c1aca558SClaudiu Beznea if (!soc_dev_attr->family) 51c1aca558SClaudiu Beznea return -ENOMEM; 52c1aca558SClaudiu Beznea 53c1aca558SClaudiu Beznea soc_dev_attr->soc_id = devm_kstrdup(sysc->dev, soc_id, GFP_KERNEL); 54c1aca558SClaudiu Beznea if (!soc_dev_attr->soc_id) 55c1aca558SClaudiu Beznea return -ENOMEM; 56c1aca558SClaudiu Beznea 57c1aca558SClaudiu Beznea val = readl(sysc->base + soc_data->devid_offset); 58c1aca558SClaudiu Beznea revision = field_get(soc_data->revision_mask, val); 59c1aca558SClaudiu Beznea specific_id = field_get(soc_data->specific_id_mask, val); 60c1aca558SClaudiu Beznea soc_dev_attr->revision = devm_kasprintf(sysc->dev, GFP_KERNEL, "%u", revision); 61c1aca558SClaudiu Beznea if (!soc_dev_attr->revision) 62c1aca558SClaudiu Beznea return -ENOMEM; 63c1aca558SClaudiu Beznea 64c1aca558SClaudiu Beznea if (soc_data->id && specific_id != soc_data->id) { 65c1aca558SClaudiu Beznea dev_warn(sysc->dev, "SoC mismatch (product = 0x%x)\n", specific_id); 66c1aca558SClaudiu Beznea return -ENODEV; 67c1aca558SClaudiu Beznea } 68c1aca558SClaudiu Beznea 69*d07470cfSJohn Madieu /* Try to call SoC-specific device identification */ 70*d07470cfSJohn Madieu if (soc_data->print_id) { 71*d07470cfSJohn Madieu soc_data->print_id(sysc->dev, sysc->base, soc_dev_attr); 72*d07470cfSJohn Madieu } else { 73*d07470cfSJohn Madieu dev_info(sysc->dev, "Detected Renesas %s %s Rev %s\n", 74*d07470cfSJohn Madieu soc_dev_attr->family, soc_dev_attr->soc_id, soc_dev_attr->revision); 75*d07470cfSJohn Madieu } 76c1aca558SClaudiu Beznea 77c1aca558SClaudiu Beznea soc_dev = soc_device_register(soc_dev_attr); 78c1aca558SClaudiu Beznea if (IS_ERR(soc_dev)) 79c1aca558SClaudiu Beznea return PTR_ERR(soc_dev); 80c1aca558SClaudiu Beznea 81c1aca558SClaudiu Beznea return 0; 82c1aca558SClaudiu Beznea } 83c1aca558SClaudiu Beznea 84c1aca558SClaudiu Beznea static const struct of_device_id rz_sysc_match[] = { 850704de89SClaudiu Beznea #ifdef CONFIG_SYSC_R9A08G045 860704de89SClaudiu Beznea { .compatible = "renesas,r9a08g045-sysc", .data = &rzg3s_sysc_init_data }, 870704de89SClaudiu Beznea #endif 88*d07470cfSJohn Madieu #ifdef CONFIG_SYS_R9A09G047 89*d07470cfSJohn Madieu { .compatible = "renesas,r9a09g047-sys", .data = &rzg3e_sys_init_data }, 90*d07470cfSJohn Madieu #endif 91c1aca558SClaudiu Beznea { } 92c1aca558SClaudiu Beznea }; 93c1aca558SClaudiu Beznea MODULE_DEVICE_TABLE(of, rz_sysc_match); 94c1aca558SClaudiu Beznea 95c1aca558SClaudiu Beznea static int rz_sysc_probe(struct platform_device *pdev) 96c1aca558SClaudiu Beznea { 97c1aca558SClaudiu Beznea const struct of_device_id *match; 98c1aca558SClaudiu Beznea struct device *dev = &pdev->dev; 99c1aca558SClaudiu Beznea struct rz_sysc *sysc; 100c1aca558SClaudiu Beznea 101c1aca558SClaudiu Beznea match = of_match_node(rz_sysc_match, dev->of_node); 102c1aca558SClaudiu Beznea if (!match) 103c1aca558SClaudiu Beznea return -ENODEV; 104c1aca558SClaudiu Beznea 105c1aca558SClaudiu Beznea sysc = devm_kzalloc(dev, sizeof(*sysc), GFP_KERNEL); 106c1aca558SClaudiu Beznea if (!sysc) 107c1aca558SClaudiu Beznea return -ENOMEM; 108c1aca558SClaudiu Beznea 109c1aca558SClaudiu Beznea sysc->base = devm_platform_ioremap_resource(pdev, 0); 110c1aca558SClaudiu Beznea if (IS_ERR(sysc->base)) 111c1aca558SClaudiu Beznea return PTR_ERR(sysc->base); 112c1aca558SClaudiu Beznea 113c1aca558SClaudiu Beznea sysc->dev = dev; 114c1aca558SClaudiu Beznea return rz_sysc_soc_init(sysc, match); 115c1aca558SClaudiu Beznea } 116c1aca558SClaudiu Beznea 117c1aca558SClaudiu Beznea static struct platform_driver rz_sysc_driver = { 118c1aca558SClaudiu Beznea .driver = { 119c1aca558SClaudiu Beznea .name = "renesas-rz-sysc", 120c1aca558SClaudiu Beznea .suppress_bind_attrs = true, 121c1aca558SClaudiu Beznea .of_match_table = rz_sysc_match 122c1aca558SClaudiu Beznea }, 123c1aca558SClaudiu Beznea .probe = rz_sysc_probe 124c1aca558SClaudiu Beznea }; 125c1aca558SClaudiu Beznea 126c1aca558SClaudiu Beznea static int __init rz_sysc_init(void) 127c1aca558SClaudiu Beznea { 128c1aca558SClaudiu Beznea return platform_driver_register(&rz_sysc_driver); 129c1aca558SClaudiu Beznea } 130c1aca558SClaudiu Beznea subsys_initcall(rz_sysc_init); 131c1aca558SClaudiu Beznea 132c1aca558SClaudiu Beznea MODULE_DESCRIPTION("Renesas RZ System Controller Driver"); 133c1aca558SClaudiu Beznea MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>"); 134c1aca558SClaudiu Beznea MODULE_LICENSE("GPL"); 135