187d68730SDong Aisheng /* 287d68730SDong Aisheng * System Control Driver 387d68730SDong Aisheng * 487d68730SDong Aisheng * Copyright (C) 2012 Freescale Semiconductor, Inc. 587d68730SDong Aisheng * Copyright (C) 2012 Linaro Ltd. 687d68730SDong Aisheng * 787d68730SDong Aisheng * Author: Dong Aisheng <[email protected]> 887d68730SDong Aisheng * 987d68730SDong Aisheng * This program is free software; you can redistribute it and/or modify 1087d68730SDong Aisheng * it under the terms of the GNU General Public License as published by 1187d68730SDong Aisheng * the Free Software Foundation; either version 2 of the License, or 1287d68730SDong Aisheng * (at your option) any later version. 1387d68730SDong Aisheng */ 1487d68730SDong Aisheng 1587d68730SDong Aisheng #include <linux/err.h> 1687d68730SDong Aisheng #include <linux/io.h> 1787d68730SDong Aisheng #include <linux/module.h> 1887d68730SDong Aisheng #include <linux/of.h> 1987d68730SDong Aisheng #include <linux/of_address.h> 2087d68730SDong Aisheng #include <linux/of_platform.h> 2187d68730SDong Aisheng #include <linux/platform_device.h> 2287d68730SDong Aisheng #include <linux/regmap.h> 2375177deeSFabio Estevam #include <linux/mfd/syscon.h> 2487d68730SDong Aisheng 2587d68730SDong Aisheng static struct platform_driver syscon_driver; 2687d68730SDong Aisheng 2787d68730SDong Aisheng struct syscon { 2887d68730SDong Aisheng void __iomem *base; 2987d68730SDong Aisheng struct regmap *regmap; 3087d68730SDong Aisheng }; 3187d68730SDong Aisheng 32*5ab3a89aSAlexander Shiyan static int syscon_match_node(struct device *dev, void *data) 3387d68730SDong Aisheng { 3487d68730SDong Aisheng struct device_node *dn = data; 3587d68730SDong Aisheng 36ed21465aSAlexander Shiyan return (dev->of_node == dn) ? 1 : 0; 3787d68730SDong Aisheng } 3887d68730SDong Aisheng 3987d68730SDong Aisheng struct regmap *syscon_node_to_regmap(struct device_node *np) 4087d68730SDong Aisheng { 4187d68730SDong Aisheng struct syscon *syscon; 4287d68730SDong Aisheng struct device *dev; 4387d68730SDong Aisheng 4487d68730SDong Aisheng dev = driver_find_device(&syscon_driver.driver, NULL, np, 45*5ab3a89aSAlexander Shiyan syscon_match_node); 4687d68730SDong Aisheng if (!dev) 4787d68730SDong Aisheng return ERR_PTR(-EPROBE_DEFER); 4887d68730SDong Aisheng 4987d68730SDong Aisheng syscon = dev_get_drvdata(dev); 5087d68730SDong Aisheng 5187d68730SDong Aisheng return syscon->regmap; 5287d68730SDong Aisheng } 5387d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 5487d68730SDong Aisheng 5587d68730SDong Aisheng struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 5687d68730SDong Aisheng { 5787d68730SDong Aisheng struct device_node *syscon_np; 5887d68730SDong Aisheng struct regmap *regmap; 5987d68730SDong Aisheng 6087d68730SDong Aisheng syscon_np = of_find_compatible_node(NULL, NULL, s); 6187d68730SDong Aisheng if (!syscon_np) 6287d68730SDong Aisheng return ERR_PTR(-ENODEV); 6387d68730SDong Aisheng 6487d68730SDong Aisheng regmap = syscon_node_to_regmap(syscon_np); 6587d68730SDong Aisheng of_node_put(syscon_np); 6687d68730SDong Aisheng 6787d68730SDong Aisheng return regmap; 6887d68730SDong Aisheng } 6987d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); 7087d68730SDong Aisheng 71*5ab3a89aSAlexander Shiyan static int syscon_match_pdevname(struct device *dev, void *data) 72*5ab3a89aSAlexander Shiyan { 73*5ab3a89aSAlexander Shiyan struct platform_device *pdev = to_platform_device(dev); 74*5ab3a89aSAlexander Shiyan const struct platform_device_id *id = platform_get_device_id(pdev); 75*5ab3a89aSAlexander Shiyan 76*5ab3a89aSAlexander Shiyan if (id) 77*5ab3a89aSAlexander Shiyan if (!strcmp(id->name, (const char *)data)) 78*5ab3a89aSAlexander Shiyan return 1; 79*5ab3a89aSAlexander Shiyan 80*5ab3a89aSAlexander Shiyan return !strcmp(dev_name(dev), (const char *)data); 81*5ab3a89aSAlexander Shiyan } 82*5ab3a89aSAlexander Shiyan 83*5ab3a89aSAlexander Shiyan struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) 84*5ab3a89aSAlexander Shiyan { 85*5ab3a89aSAlexander Shiyan struct device *dev; 86*5ab3a89aSAlexander Shiyan struct syscon *syscon; 87*5ab3a89aSAlexander Shiyan 88*5ab3a89aSAlexander Shiyan dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, 89*5ab3a89aSAlexander Shiyan syscon_match_pdevname); 90*5ab3a89aSAlexander Shiyan if (!dev) 91*5ab3a89aSAlexander Shiyan return ERR_PTR(-EPROBE_DEFER); 92*5ab3a89aSAlexander Shiyan 93*5ab3a89aSAlexander Shiyan syscon = dev_get_drvdata(dev); 94*5ab3a89aSAlexander Shiyan 95*5ab3a89aSAlexander Shiyan return syscon->regmap; 96*5ab3a89aSAlexander Shiyan } 97*5ab3a89aSAlexander Shiyan EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); 98*5ab3a89aSAlexander Shiyan 9987d68730SDong Aisheng struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 10087d68730SDong Aisheng const char *property) 10187d68730SDong Aisheng { 10287d68730SDong Aisheng struct device_node *syscon_np; 10387d68730SDong Aisheng struct regmap *regmap; 10487d68730SDong Aisheng 10587d68730SDong Aisheng syscon_np = of_parse_phandle(np, property, 0); 10687d68730SDong Aisheng if (!syscon_np) 10787d68730SDong Aisheng return ERR_PTR(-ENODEV); 10887d68730SDong Aisheng 10987d68730SDong Aisheng regmap = syscon_node_to_regmap(syscon_np); 11087d68730SDong Aisheng of_node_put(syscon_np); 11187d68730SDong Aisheng 11287d68730SDong Aisheng return regmap; 11387d68730SDong Aisheng } 11487d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 11587d68730SDong Aisheng 11687d68730SDong Aisheng static const struct of_device_id of_syscon_match[] = { 11787d68730SDong Aisheng { .compatible = "syscon", }, 11887d68730SDong Aisheng { }, 11987d68730SDong Aisheng }; 12087d68730SDong Aisheng 12187d68730SDong Aisheng static struct regmap_config syscon_regmap_config = { 12287d68730SDong Aisheng .reg_bits = 32, 12387d68730SDong Aisheng .val_bits = 32, 12487d68730SDong Aisheng .reg_stride = 4, 12587d68730SDong Aisheng }; 12687d68730SDong Aisheng 127f791be49SBill Pemberton static int syscon_probe(struct platform_device *pdev) 12887d68730SDong Aisheng { 12987d68730SDong Aisheng struct device *dev = &pdev->dev; 13087d68730SDong Aisheng struct syscon *syscon; 131*5ab3a89aSAlexander Shiyan struct resource *res; 13287d68730SDong Aisheng 133*5ab3a89aSAlexander Shiyan syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); 13487d68730SDong Aisheng if (!syscon) 13587d68730SDong Aisheng return -ENOMEM; 13687d68730SDong Aisheng 137*5ab3a89aSAlexander Shiyan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138*5ab3a89aSAlexander Shiyan if (!res) 139*5ab3a89aSAlexander Shiyan return -ENOENT; 140*5ab3a89aSAlexander Shiyan 141*5ab3a89aSAlexander Shiyan syscon->base = devm_ioremap(dev, res->start, resource_size(res)); 14287d68730SDong Aisheng if (!syscon->base) 143*5ab3a89aSAlexander Shiyan return -ENOMEM; 14487d68730SDong Aisheng 145*5ab3a89aSAlexander Shiyan syscon_regmap_config.max_register = res->end - res->start - 3; 14687d68730SDong Aisheng syscon->regmap = devm_regmap_init_mmio(dev, syscon->base, 14787d68730SDong Aisheng &syscon_regmap_config); 14887d68730SDong Aisheng if (IS_ERR(syscon->regmap)) { 14987d68730SDong Aisheng dev_err(dev, "regmap init failed\n"); 15087d68730SDong Aisheng return PTR_ERR(syscon->regmap); 15187d68730SDong Aisheng } 15287d68730SDong Aisheng 15387d68730SDong Aisheng platform_set_drvdata(pdev, syscon); 15487d68730SDong Aisheng 155*5ab3a89aSAlexander Shiyan dev_info(dev, "regmap 0x%x-0x%x registered\n", res->start, res->end); 15687d68730SDong Aisheng 15787d68730SDong Aisheng return 0; 15887d68730SDong Aisheng } 15987d68730SDong Aisheng 160*5ab3a89aSAlexander Shiyan static const struct platform_device_id syscon_ids[] = { 161*5ab3a89aSAlexander Shiyan { "syscon", }, 162*5ab3a89aSAlexander Shiyan { } 163*5ab3a89aSAlexander Shiyan }; 16487d68730SDong Aisheng 16587d68730SDong Aisheng static struct platform_driver syscon_driver = { 16687d68730SDong Aisheng .driver = { 16787d68730SDong Aisheng .name = "syscon", 16887d68730SDong Aisheng .owner = THIS_MODULE, 16987d68730SDong Aisheng .of_match_table = of_syscon_match, 17087d68730SDong Aisheng }, 17187d68730SDong Aisheng .probe = syscon_probe, 172*5ab3a89aSAlexander Shiyan .id_table = syscon_ids, 17387d68730SDong Aisheng }; 17487d68730SDong Aisheng 17587d68730SDong Aisheng static int __init syscon_init(void) 17687d68730SDong Aisheng { 17787d68730SDong Aisheng return platform_driver_register(&syscon_driver); 17887d68730SDong Aisheng } 17987d68730SDong Aisheng postcore_initcall(syscon_init); 18087d68730SDong Aisheng 18187d68730SDong Aisheng static void __exit syscon_exit(void) 18287d68730SDong Aisheng { 18387d68730SDong Aisheng platform_driver_unregister(&syscon_driver); 18487d68730SDong Aisheng } 18587d68730SDong Aisheng module_exit(syscon_exit); 18687d68730SDong Aisheng 18787d68730SDong Aisheng MODULE_AUTHOR("Dong Aisheng <[email protected]>"); 18887d68730SDong Aisheng MODULE_DESCRIPTION("System Control driver"); 18987d68730SDong Aisheng MODULE_LICENSE("GPL v2"); 190