123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- /* Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) "ACC: %s: " fmt, __func__
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/err.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/io.h>
- #include <linux/platform_device.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- #include <linux/regulator/of_regulator.h>
- #define MEM_ACC_SEL_MASK 0x3
- enum {
- MEMORY_L1,
- MEMORY_L2,
- MEMORY_MAX,
- };
- struct mem_acc_regulator {
- struct device *dev;
- struct regulator_desc rdesc;
- struct regulator_dev *rdev;
- int corner;
- bool mem_acc_supported[MEMORY_MAX];
- u32 acc_sel_reg[MEMORY_MAX];
- u32 *acc_sel_mask[MEMORY_MAX];
- u32 *acc_sel_bit_pos[MEMORY_MAX];
- u32 num_acc_sel[MEMORY_MAX];
- u32 *acc_en_bit_pos;
- u32 num_acc_en;
- u32 *corner_acc_map;
- u32 num_corners;
- void __iomem *acc_sel_base[MEMORY_MAX];
- void __iomem *acc_en_base;
- phys_addr_t acc_sel_addr[MEMORY_MAX];
- phys_addr_t acc_en_addr;
- };
- static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg,
- int corner)
- {
- /*
- * corner_acc_map maps the corner from index 0 and APC corner value
- * starts from the value 1
- */
- return mem_acc_vreg->corner_acc_map[corner - 1];
- }
- static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg,
- int corner, int mem_type)
- {
- u32 acc_data, i, bit, acc_corner;
- acc_data = mem_acc_vreg->acc_sel_reg[mem_type];
- for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
- bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
- acc_data &= ~mem_acc_vreg->acc_sel_mask[mem_type][i];
- acc_corner = apc_to_acc_corner(mem_acc_vreg, corner);
- acc_data |= (acc_corner << bit) &
- mem_acc_vreg->acc_sel_mask[mem_type][i];
- }
- pr_debug("corner=%d old_acc_sel=0x%02x new_acc_sel=0x%02x mem_type=%d\n",
- corner, mem_acc_vreg->acc_sel_reg[mem_type],
- acc_data, mem_type);
- writel_relaxed(acc_data, mem_acc_vreg->acc_sel_base[mem_type]);
- mem_acc_vreg->acc_sel_reg[mem_type] = acc_data;
- }
- static void update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, int corner)
- {
- int i;
- for (i = 0; i < MEMORY_MAX; i++) {
- if (mem_acc_vreg->mem_acc_supported[i])
- __update_acc_sel(mem_acc_vreg, corner, i);
- }
- }
- static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev,
- int corner, int corner_max, unsigned *selector)
- {
- struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
- int i;
- if (corner > mem_acc_vreg->num_corners) {
- pr_err("Invalid corner=%d requested\n", corner);
- return -EINVAL;
- }
- pr_debug("old corner=%d, new corner=%d\n",
- mem_acc_vreg->corner, corner);
- if (corner == mem_acc_vreg->corner)
- return 0;
- /* go up or down one level at a time */
- if (corner > mem_acc_vreg->corner) {
- for (i = mem_acc_vreg->corner + 1; i <= corner; i++) {
- pr_debug("UP: to corner %d\n", i);
- update_acc_sel(mem_acc_vreg, i);
- }
- } else {
- for (i = mem_acc_vreg->corner - 1; i >= corner; i--) {
- pr_debug("DOWN: to corner %d\n", i);
- update_acc_sel(mem_acc_vreg, i);
- }
- }
- pr_debug("new voltage corner set %d\n", corner);
- mem_acc_vreg->corner = corner;
- return 0;
- }
- static int mem_acc_regulator_get_voltage(struct regulator_dev *rdev)
- {
- struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
- return mem_acc_vreg->corner;
- }
- static struct regulator_ops mem_acc_corner_ops = {
- .set_voltage = mem_acc_regulator_set_voltage,
- .get_voltage = mem_acc_regulator_get_voltage,
- };
- static int __mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg,
- int mem_type)
- {
- int i;
- u32 bit;
- mem_acc_vreg->acc_sel_mask[mem_type] = devm_kzalloc(mem_acc_vreg->dev,
- mem_acc_vreg->num_acc_sel[mem_type] * sizeof(u32), GFP_KERNEL);
- if (!mem_acc_vreg->acc_sel_mask[mem_type]) {
- pr_err("Unable to allocate memory for mem_type=%d\n", mem_type);
- return -ENOMEM;
- }
- for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
- bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
- mem_acc_vreg->acc_sel_mask[mem_type][i] =
- MEM_ACC_SEL_MASK << bit;
- }
- mem_acc_vreg->acc_sel_reg[mem_type] =
- readl_relaxed(mem_acc_vreg->acc_sel_base[mem_type]);
- return 0;
- }
- static int mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg)
- {
- int i, rc;
- for (i = 0; i < MEMORY_MAX; i++) {
- if (mem_acc_vreg->mem_acc_supported[i]) {
- rc = __mem_acc_sel_init(mem_acc_vreg, i);
- if (rc) {
- pr_err("Unable to intialize mem_type=%d rc=%d\n",
- i, rc);
- return rc;
- }
- }
- }
- return 0;
- }
- static void mem_acc_en_init(struct mem_acc_regulator *mem_acc_vreg)
- {
- int i, bit;
- u32 acc_data;
- acc_data = readl_relaxed(mem_acc_vreg->acc_en_base);
- pr_debug("init: acc_en_register=%x\n", acc_data);
- for (i = 0; i < mem_acc_vreg->num_acc_en; i++) {
- bit = mem_acc_vreg->acc_en_bit_pos[i];
- acc_data |= BIT(bit);
- }
- pr_debug("final: acc_en_register=%x\n", acc_data);
- writel_relaxed(acc_data, mem_acc_vreg->acc_en_base);
- }
- static int populate_acc_data(struct mem_acc_regulator *mem_acc_vreg,
- const char *prop_name, u32 **value, u32 *len)
- {
- int rc;
- if (!of_get_property(mem_acc_vreg->dev->of_node, prop_name, len)) {
- pr_err("Unable to find %s property\n", prop_name);
- return -EINVAL;
- }
- *len /= sizeof(u32);
- if (!(*len)) {
- pr_err("Incorrect entries in %s\n", prop_name);
- return -EINVAL;
- }
- *value = devm_kzalloc(mem_acc_vreg->dev, (*len) * sizeof(u32),
- GFP_KERNEL);
- if (!(*value)) {
- pr_err("Unable to allocate memory for %s\n", prop_name);
- return -ENOMEM;
- }
- pr_debug("Found %s, data-length = %d\n", prop_name, *len);
- rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
- prop_name, *value, *len);
- if (rc) {
- pr_err("Unable to populate %s rc=%d\n", prop_name, rc);
- return rc;
- }
- return 0;
- }
- static int mem_acc_sel_setup(struct mem_acc_regulator *mem_acc_vreg,
- struct resource *res, int mem_type)
- {
- int len, rc;
- char *mem_select_str;
- mem_acc_vreg->acc_sel_addr[mem_type] = res->start;
- len = res->end - res->start + 1;
- pr_debug("'acc_sel_addr' = %pa mem_type=%d (len=%d)\n",
- &res->start, mem_type, len);
- mem_acc_vreg->acc_sel_base[mem_type] = devm_ioremap(mem_acc_vreg->dev,
- mem_acc_vreg->acc_sel_addr[mem_type], len);
- if (!mem_acc_vreg->acc_sel_base[mem_type]) {
- pr_err("Unable to map 'acc_sel_addr' %pa for mem_type=%d\n",
- &mem_acc_vreg->acc_sel_addr[mem_type], mem_type);
- return -EINVAL;
- }
- switch (mem_type) {
- case MEMORY_L1:
- mem_select_str = "qcom,acc-sel-l1-bit-pos";
- break;
- case MEMORY_L2:
- mem_select_str = "qcom,acc-sel-l2-bit-pos";
- break;
- }
- rc = populate_acc_data(mem_acc_vreg, mem_select_str,
- &mem_acc_vreg->acc_sel_bit_pos[mem_type],
- &mem_acc_vreg->num_acc_sel[mem_type]);
- if (rc)
- pr_err("Unable to populate '%s' rc=%d\n", mem_select_str, rc);
- return rc;
- }
- static int mem_acc_init(struct platform_device *pdev,
- struct mem_acc_regulator *mem_acc_vreg)
- {
- struct resource *res;
- int len, rc, i;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-en");
- if (!res || !res->start) {
- pr_debug("'acc-en' resource missing or not used.\n");
- } else {
- mem_acc_vreg->acc_en_addr = res->start;
- len = res->end - res->start + 1;
- pr_debug("'acc_en_addr' = %pa (len=0x%x)\n", &res->start, len);
- mem_acc_vreg->acc_en_base = devm_ioremap(mem_acc_vreg->dev,
- mem_acc_vreg->acc_en_addr, len);
- if (!mem_acc_vreg->acc_en_base) {
- pr_err("Unable to map 'acc_en_addr' %pa\n",
- &mem_acc_vreg->acc_en_addr);
- return -EINVAL;
- }
- rc = populate_acc_data(mem_acc_vreg, "qcom,acc-en-bit-pos",
- &mem_acc_vreg->acc_en_bit_pos,
- &mem_acc_vreg->num_acc_en);
- if (rc) {
- pr_err("Unable to populate 'qcom,acc-en-bit-pos' rc=%d\n",
- rc);
- return rc;
- }
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l1");
- if (!res || !res->start) {
- pr_debug("'acc-sel-l1' resource missing or not used.\n");
- } else {
- rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L1);
- if (rc) {
- pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
- MEMORY_L1, rc);
- return rc;
- }
- mem_acc_vreg->mem_acc_supported[MEMORY_L1] = true;
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l2");
- if (!res || !res->start) {
- pr_debug("'acc-sel-l2' resource missing or not used.\n");
- } else {
- rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L2);
- if (rc) {
- pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
- MEMORY_L2, rc);
- return rc;
- }
- mem_acc_vreg->mem_acc_supported[MEMORY_L2] = true;
- }
- rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map",
- &mem_acc_vreg->corner_acc_map,
- &mem_acc_vreg->num_corners);
- if (rc) {
- pr_err("Unable to find 'qcom,corner-acc-map' rc=%d\n", rc);
- return rc;
- }
- pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners);
- /* Check if at least one valid mem-acc config. is specified */
- for (i = 0; i < MEMORY_MAX; i++) {
- if (mem_acc_vreg->mem_acc_supported[i])
- break;
- }
- if (i == MEMORY_MAX) {
- pr_err("No mem-acc configuration specified\n");
- return -EINVAL;
- }
- if (mem_acc_vreg->num_acc_en)
- mem_acc_en_init(mem_acc_vreg);
- rc = mem_acc_sel_init(mem_acc_vreg);
- if (rc) {
- pr_err("Unable to intialize mem_acc_sel reg rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- static int mem_acc_regulator_probe(struct platform_device *pdev)
- {
- struct mem_acc_regulator *mem_acc_vreg;
- struct regulator_desc *rdesc;
- struct regulator_init_data *init_data;
- int rc;
- if (!pdev->dev.of_node) {
- pr_err("Device tree node is missing\n");
- return -EINVAL;
- }
- init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
- if (!init_data) {
- pr_err("regulator init data is missing\n");
- return -EINVAL;
- } else {
- init_data->constraints.input_uV
- = init_data->constraints.max_uV;
- init_data->constraints.valid_ops_mask
- |= REGULATOR_CHANGE_VOLTAGE;
- }
- mem_acc_vreg = devm_kzalloc(&pdev->dev, sizeof(*mem_acc_vreg),
- GFP_KERNEL);
- if (!mem_acc_vreg) {
- pr_err("Can't allocate mem_acc_vreg memory\n");
- return -ENOMEM;
- }
- mem_acc_vreg->dev = &pdev->dev;
- rc = mem_acc_init(pdev, mem_acc_vreg);
- if (rc) {
- pr_err("Unable to initialize mem_acc configuration rc=%d\n",
- rc);
- return rc;
- }
- rdesc = &mem_acc_vreg->rdesc;
- rdesc->owner = THIS_MODULE;
- rdesc->type = REGULATOR_VOLTAGE;
- rdesc->ops = &mem_acc_corner_ops;
- rdesc->name = init_data->constraints.name;
- mem_acc_vreg->rdev = regulator_register(rdesc, &pdev->dev,
- init_data, mem_acc_vreg, pdev->dev.of_node);
- if (IS_ERR(mem_acc_vreg->rdev)) {
- rc = PTR_ERR(mem_acc_vreg->rdev);
- if (rc != -EPROBE_DEFER)
- pr_err("regulator_register failed: rc=%d\n", rc);
- return rc;
- }
- platform_set_drvdata(pdev, mem_acc_vreg);
- return 0;
- }
- static int mem_acc_regulator_remove(struct platform_device *pdev)
- {
- struct mem_acc_regulator *mem_acc_vreg = platform_get_drvdata(pdev);
- regulator_unregister(mem_acc_vreg->rdev);
- return 0;
- }
- static struct of_device_id mem_acc_regulator_match_table[] = {
- { .compatible = "qcom,mem-acc-regulator", },
- {}
- };
- static struct platform_driver mem_acc_regulator_driver = {
- .probe = mem_acc_regulator_probe,
- .remove = mem_acc_regulator_remove,
- .driver = {
- .name = "qcom,mem-acc-regulator",
- .of_match_table = mem_acc_regulator_match_table,
- .owner = THIS_MODULE,
- },
- };
- int __init mem_acc_regulator_init(void)
- {
- return platform_driver_register(&mem_acc_regulator_driver);
- }
- postcore_initcall(mem_acc_regulator_init);
- static void __exit mem_acc_regulator_exit(void)
- {
- platform_driver_unregister(&mem_acc_regulator_driver);
- }
- module_exit(mem_acc_regulator_exit);
- MODULE_DESCRIPTION("MEM-ACC-SEL regulator driver");
- MODULE_LICENSE("GPL v2");
|