linux/drivers/misc/atmel-ssc.c
<<
>>
Prefs
   1/*
   2 * Atmel SSC driver
   3 *
   4 * Copyright (C) 2007 Atmel Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/platform_device.h>
  12#include <linux/list.h>
  13#include <linux/clk.h>
  14#include <linux/err.h>
  15#include <linux/io.h>
  16#include <linux/spinlock.h>
  17#include <linux/atmel-ssc.h>
  18#include <linux/slab.h>
  19#include <linux/module.h>
  20
  21#include <linux/of.h>
  22#include <linux/pinctrl/consumer.h>
  23
  24/* Serialize access to ssc_list and user count */
  25static DEFINE_SPINLOCK(user_lock);
  26static LIST_HEAD(ssc_list);
  27
  28struct ssc_device *ssc_request(unsigned int ssc_num)
  29{
  30        int ssc_valid = 0;
  31        struct ssc_device *ssc;
  32
  33        spin_lock(&user_lock);
  34        list_for_each_entry(ssc, &ssc_list, list) {
  35                if (ssc->pdev->dev.of_node) {
  36                        if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
  37                                == ssc_num) {
  38                                ssc_valid = 1;
  39                                break;
  40                        }
  41                } else if (ssc->pdev->id == ssc_num) {
  42                        ssc_valid = 1;
  43                        break;
  44                }
  45        }
  46
  47        if (!ssc_valid) {
  48                spin_unlock(&user_lock);
  49                pr_err("ssc: ssc%d platform device is missing\n", ssc_num);
  50                return ERR_PTR(-ENODEV);
  51        }
  52
  53        if (ssc->user) {
  54                spin_unlock(&user_lock);
  55                dev_dbg(&ssc->pdev->dev, "module busy\n");
  56                return ERR_PTR(-EBUSY);
  57        }
  58        ssc->user++;
  59        spin_unlock(&user_lock);
  60
  61        clk_enable(ssc->clk);
  62
  63        return ssc;
  64}
  65EXPORT_SYMBOL(ssc_request);
  66
  67void ssc_free(struct ssc_device *ssc)
  68{
  69        spin_lock(&user_lock);
  70        if (ssc->user) {
  71                ssc->user--;
  72                clk_disable(ssc->clk);
  73        } else {
  74                dev_dbg(&ssc->pdev->dev, "device already free\n");
  75        }
  76        spin_unlock(&user_lock);
  77}
  78EXPORT_SYMBOL(ssc_free);
  79
  80static struct atmel_ssc_platform_data at91rm9200_config = {
  81        .use_dma = 0,
  82};
  83
  84static struct atmel_ssc_platform_data at91sam9g45_config = {
  85        .use_dma = 1,
  86};
  87
  88static const struct platform_device_id atmel_ssc_devtypes[] = {
  89        {
  90                .name = "at91rm9200_ssc",
  91                .driver_data = (unsigned long) &at91rm9200_config,
  92        }, {
  93                .name = "at91sam9g45_ssc",
  94                .driver_data = (unsigned long) &at91sam9g45_config,
  95        }, {
  96                /* sentinel */
  97        }
  98};
  99
 100#ifdef CONFIG_OF
 101static const struct of_device_id atmel_ssc_dt_ids[] = {
 102        {
 103                .compatible = "atmel,at91rm9200-ssc",
 104                .data = &at91rm9200_config,
 105        }, {
 106                .compatible = "atmel,at91sam9g45-ssc",
 107                .data = &at91sam9g45_config,
 108        }, {
 109                /* sentinel */
 110        }
 111};
 112MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
 113#endif
 114
 115static inline const struct atmel_ssc_platform_data * __init
 116        atmel_ssc_get_driver_data(struct platform_device *pdev)
 117{
 118        if (pdev->dev.of_node) {
 119                const struct of_device_id *match;
 120                match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
 121                if (match == NULL)
 122                        return NULL;
 123                return match->data;
 124        }
 125
 126        return (struct atmel_ssc_platform_data *)
 127                platform_get_device_id(pdev)->driver_data;
 128}
 129
 130static int ssc_probe(struct platform_device *pdev)
 131{
 132        struct resource *regs;
 133        struct ssc_device *ssc;
 134        const struct atmel_ssc_platform_data *plat_dat;
 135        struct pinctrl *pinctrl;
 136
 137        pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 138        if (IS_ERR(pinctrl)) {
 139                dev_err(&pdev->dev, "Failed to request pinctrl\n");
 140                return PTR_ERR(pinctrl);
 141        }
 142
 143        ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
 144        if (!ssc) {
 145                dev_dbg(&pdev->dev, "out of memory\n");
 146                return -ENOMEM;
 147        }
 148
 149        ssc->pdev = pdev;
 150
 151        plat_dat = atmel_ssc_get_driver_data(pdev);
 152        if (!plat_dat)
 153                return -ENODEV;
 154        ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
 155
 156        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 157        ssc->regs = devm_ioremap_resource(&pdev->dev, regs);
 158        if (IS_ERR(ssc->regs))
 159                return PTR_ERR(ssc->regs);
 160
 161        ssc->phybase = regs->start;
 162
 163        ssc->clk = devm_clk_get(&pdev->dev, "pclk");
 164        if (IS_ERR(ssc->clk)) {
 165                dev_dbg(&pdev->dev, "no pclk clock defined\n");
 166                return -ENXIO;
 167        }
 168
 169        /* disable all interrupts */
 170        clk_enable(ssc->clk);
 171        ssc_writel(ssc->regs, IDR, -1);
 172        ssc_readl(ssc->regs, SR);
 173        clk_disable(ssc->clk);
 174
 175        ssc->irq = platform_get_irq(pdev, 0);
 176        if (!ssc->irq) {
 177                dev_dbg(&pdev->dev, "could not get irq\n");
 178                return -ENXIO;
 179        }
 180
 181        spin_lock(&user_lock);
 182        list_add_tail(&ssc->list, &ssc_list);
 183        spin_unlock(&user_lock);
 184
 185        platform_set_drvdata(pdev, ssc);
 186
 187        dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
 188                        ssc->regs, ssc->irq);
 189
 190        return 0;
 191}
 192
 193static int ssc_remove(struct platform_device *pdev)
 194{
 195        struct ssc_device *ssc = platform_get_drvdata(pdev);
 196
 197        spin_lock(&user_lock);
 198        list_del(&ssc->list);
 199        spin_unlock(&user_lock);
 200
 201        return 0;
 202}
 203
 204static struct platform_driver ssc_driver = {
 205        .driver         = {
 206                .name           = "ssc",
 207                .owner          = THIS_MODULE,
 208                .of_match_table = of_match_ptr(atmel_ssc_dt_ids),
 209        },
 210        .id_table       = atmel_ssc_devtypes,
 211        .probe          = ssc_probe,
 212        .remove         = ssc_remove,
 213};
 214module_platform_driver(ssc_driver);
 215
 216MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
 217MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
 218MODULE_LICENSE("GPL");
 219MODULE_ALIAS("platform:ssc");
 220