/* * System Control Driver * * Copyright (C) 2012 Freescale Semiconductor, Inc. * Copyright (C) 2012 Linaro Ltd. * * Author: Dong Aisheng * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include static struct platform_driver syscon_driver; struct syscon { struct device *dev; void __iomem *base; struct regmap *regmap; }; static int syscon_match(struct device *dev, void *data) { struct syscon *syscon = dev_get_drvdata(dev); struct device_node *dn = data; return (syscon->dev->of_node == dn) ? 1 : 0; } struct regmap *syscon_node_to_regmap(struct device_node *np) { struct syscon *syscon; struct device *dev; dev = driver_find_device(&syscon_driver.driver, NULL, np, syscon_match); if (!dev) return ERR_PTR(-EPROBE_DEFER); syscon = dev_get_drvdata(dev); return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_node_to_regmap); struct regmap *syscon_regmap_lookup_by_compatible(const char *s) { struct device_node *syscon_np; struct regmap *regmap; syscon_np = of_find_compatible_node(NULL, NULL, s); if (!syscon_np) return ERR_PTR(-ENODEV); regmap = syscon_node_to_regmap(syscon_np); of_node_put(syscon_np); return regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, const char *property) { struct device_node *syscon_np; struct regmap *regmap; syscon_np = of_parse_phandle(np, property, 0); if (!syscon_np) return ERR_PTR(-ENODEV); regmap = syscon_node_to_regmap(syscon_np); of_node_put(syscon_np); return regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); static const struct of_device_id of_syscon_match[] = { { .compatible = "syscon", }, { }, }; static struct regmap_config syscon_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, }; static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct syscon *syscon; struct resource res; int ret; if (!np) return -ENOENT; syscon = devm_kzalloc(dev, sizeof(struct syscon), GFP_KERNEL); if (!syscon) return -ENOMEM; syscon->base = of_iomap(np, 0); if (!syscon->base) return -EADDRNOTAVAIL; ret = of_address_to_resource(np, 0, &res); if (ret) return ret; syscon_regmap_config.max_register = res.end - res.start - 3; syscon->regmap = devm_regmap_init_mmio(dev, syscon->base, &syscon_regmap_config); if (IS_ERR(syscon->regmap)) { dev_err(dev, "regmap init failed\n"); return PTR_ERR(syscon->regmap); } syscon->dev = dev; platform_set_drvdata(pdev, syscon); dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", res.start, res.end); return 0; } static int syscon_remove(struct platform_device *pdev) { struct syscon *syscon; syscon = platform_get_drvdata(pdev); iounmap(syscon->base); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver syscon_driver = { .driver = { .name = "syscon", .owner = THIS_MODULE, .of_match_table = of_syscon_match, }, .probe = syscon_probe, .remove = syscon_remove, }; static int __init syscon_init(void) { return platform_driver_register(&syscon_driver); } postcore_initcall(syscon_init); static void __exit syscon_exit(void) { platform_driver_unregister(&syscon_driver); } module_exit(syscon_exit); MODULE_AUTHOR("Dong Aisheng "); MODULE_DESCRIPTION("System Control driver"); MODULE_LICENSE("GPL v2");