linux/drivers/pinctrl/pinctrl-da850-pupd.c
<<
>>
Prefs
   1/*
   2 * Pinconf driver for TI DA850/OMAP-L138/AM18XX pullup/pulldown groups
   3 *
   4 * Copyright (C) 2016  David Lechner
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the Free
   8 * Software Foundation; version 2 of the License.
   9 *
  10 * This program is distributed in the hope that it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 */
  15
  16#include <linux/bitops.h>
  17#include <linux/device.h>
  18#include <linux/io.h>
  19#include <linux/ioport.h>
  20#include <linux/mod_devicetable.h>
  21#include <linux/module.h>
  22#include <linux/pinctrl/pinconf.h>
  23#include <linux/pinctrl/pinconf-generic.h>
  24#include <linux/pinctrl/pinctrl.h>
  25#include <linux/platform_device.h>
  26
  27#define DA850_PUPD_ENA          0x00
  28#define DA850_PUPD_SEL          0x04
  29
  30struct da850_pupd_data {
  31        void __iomem *base;
  32        struct pinctrl_desc desc;
  33        struct pinctrl_dev *pinctrl;
  34};
  35
  36static const char * const da850_pupd_group_names[] = {
  37        "cp0", "cp1", "cp2", "cp3", "cp4", "cp5", "cp6", "cp7",
  38        "cp8", "cp9", "cp10", "cp11", "cp12", "cp13", "cp14", "cp15",
  39        "cp16", "cp17", "cp18", "cp19", "cp20", "cp21", "cp22", "cp23",
  40        "cp24", "cp25", "cp26", "cp27", "cp28", "cp29", "cp30", "cp31",
  41};
  42
  43static int da850_pupd_get_groups_count(struct pinctrl_dev *pctldev)
  44{
  45        return ARRAY_SIZE(da850_pupd_group_names);
  46}
  47
  48static const char *da850_pupd_get_group_name(struct pinctrl_dev *pctldev,
  49                                             unsigned int selector)
  50{
  51        return da850_pupd_group_names[selector];
  52}
  53
  54static int da850_pupd_get_group_pins(struct pinctrl_dev *pctldev,
  55                                     unsigned int selector,
  56                                     const unsigned int **pins,
  57                                     unsigned int *num_pins)
  58{
  59        *num_pins = 0;
  60
  61        return 0;
  62}
  63
  64static const struct pinctrl_ops da850_pupd_pctlops = {
  65        .get_groups_count       = da850_pupd_get_groups_count,
  66        .get_group_name         = da850_pupd_get_group_name,
  67        .get_group_pins         = da850_pupd_get_group_pins,
  68        .dt_node_to_map         = pinconf_generic_dt_node_to_map_group,
  69        .dt_free_map            = pinconf_generic_dt_free_map,
  70};
  71
  72static int da850_pupd_pin_config_group_get(struct pinctrl_dev *pctldev,
  73                                           unsigned int selector,
  74                                           unsigned long *config)
  75{
  76        struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev);
  77        enum pin_config_param param = pinconf_to_config_param(*config);
  78        u32 val;
  79        u16 arg;
  80
  81        val = readl(data->base + DA850_PUPD_ENA);
  82        arg = !!(~val & BIT(selector));
  83
  84        switch (param) {
  85        case PIN_CONFIG_BIAS_DISABLE:
  86                break;
  87        case PIN_CONFIG_BIAS_PULL_UP:
  88        case PIN_CONFIG_BIAS_PULL_DOWN:
  89                if (arg) {
  90                        /* bias is disabled */
  91                        arg = 0;
  92                        break;
  93                }
  94                val = readl(data->base + DA850_PUPD_SEL);
  95                if (param == PIN_CONFIG_BIAS_PULL_DOWN)
  96                        val = ~val;
  97                arg = !!(val & BIT(selector));
  98                break;
  99        default:
 100                return -EINVAL;
 101        }
 102
 103        *config = pinconf_to_config_packed(param, arg);
 104
 105        return 0;
 106}
 107
 108static int da850_pupd_pin_config_group_set(struct pinctrl_dev *pctldev,
 109                                           unsigned int selector,
 110                                           unsigned long *configs,
 111                                           unsigned int num_configs)
 112{
 113        struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev);
 114        u32 ena, sel;
 115        enum pin_config_param param;
 116        int i;
 117
 118        ena = readl(data->base + DA850_PUPD_ENA);
 119        sel = readl(data->base + DA850_PUPD_SEL);
 120
 121        for (i = 0; i < num_configs; i++) {
 122                param = pinconf_to_config_param(configs[i]);
 123
 124                switch (param) {
 125                case PIN_CONFIG_BIAS_DISABLE:
 126                        ena &= ~BIT(selector);
 127                        break;
 128                case PIN_CONFIG_BIAS_PULL_UP:
 129                        ena |= BIT(selector);
 130                        sel |= BIT(selector);
 131                        break;
 132                case PIN_CONFIG_BIAS_PULL_DOWN:
 133                        ena |= BIT(selector);
 134                        sel &= ~BIT(selector);
 135                        break;
 136                default:
 137                        return -EINVAL;
 138                }
 139        }
 140
 141        writel(sel, data->base + DA850_PUPD_SEL);
 142        writel(ena, data->base + DA850_PUPD_ENA);
 143
 144        return 0;
 145}
 146
 147static const struct pinconf_ops da850_pupd_confops = {
 148        .is_generic             = true,
 149        .pin_config_group_get   = da850_pupd_pin_config_group_get,
 150        .pin_config_group_set   = da850_pupd_pin_config_group_set,
 151};
 152
 153static int da850_pupd_probe(struct platform_device *pdev)
 154{
 155        struct device *dev = &pdev->dev;
 156        struct da850_pupd_data *data;
 157        struct resource *res;
 158
 159        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 160        if (!data)
 161                return -ENOMEM;
 162
 163        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 164        data->base = devm_ioremap_resource(dev, res);
 165        if (IS_ERR(data->base)) {
 166                dev_err(dev, "Could not map resource\n");
 167                return PTR_ERR(data->base);
 168        }
 169
 170        data->desc.name = dev_name(dev);
 171        data->desc.pctlops = &da850_pupd_pctlops;
 172        data->desc.confops = &da850_pupd_confops;
 173        data->desc.owner = THIS_MODULE;
 174
 175        data->pinctrl = devm_pinctrl_register(dev, &data->desc, data);
 176        if (IS_ERR(data->pinctrl)) {
 177                dev_err(dev, "Failed to register pinctrl\n");
 178                return PTR_ERR(data->pinctrl);
 179        }
 180
 181        platform_set_drvdata(pdev, data);
 182
 183        return 0;
 184}
 185
 186static int da850_pupd_remove(struct platform_device *pdev)
 187{
 188        return 0;
 189}
 190
 191static const struct of_device_id da850_pupd_of_match[] = {
 192        { .compatible = "ti,da850-pupd" },
 193        { }
 194};
 195MODULE_DEVICE_TABLE(of, da850_pupd_of_match);
 196
 197static struct platform_driver da850_pupd_driver = {
 198        .driver = {
 199                .name           = "ti-da850-pupd",
 200                .of_match_table = da850_pupd_of_match,
 201        },
 202        .probe  = da850_pupd_probe,
 203        .remove = da850_pupd_remove,
 204};
 205module_platform_driver(da850_pupd_driver);
 206
 207MODULE_AUTHOR("David Lechner <david@lechnology.com>");
 208MODULE_DESCRIPTION("TI DA850/OMAP-L138/AM18XX pullup/pulldown configuration");
 209MODULE_LICENSE("GPL");
 210