1/* 2 * Moschip MCS814x GPIO support 3 * 4 * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org> 5 * 6 * Licensed under the GPLv2 7 */ 8 9#include <linux/init.h> 10#include <linux/module.h> 11#include <linux/kernel.h> 12#include <linux/slab.h> 13#include <linux/platform_device.h> 14#include <linux/gpio.h> 15#include <linux/io.h> 16#include <linux/of.h> 17#include <linux/of_address.h> 18 19struct mcs814x_gpio_chip { 20 void __iomem *regs; 21 struct gpio_chip chip; 22}; 23 24#define GPIO_PIN 0x00 25#define GPIO_DIR 0x04 26 27#define to_mcs814x_gpio_chip(x) container_of(x, struct mcs814x_gpio_chip, chip) 28 29static int mcs814x_gpio_get(struct gpio_chip *chip, unsigned offset) 30{ 31 struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip); 32 33 return readl_relaxed(mcs814x->regs + GPIO_PIN) & (1 << offset); 34} 35 36static void mcs814x_gpio_set(struct gpio_chip *chip, 37 unsigned offset, int value) 38{ 39 struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip); 40 u32 mask; 41 42 mask = readl_relaxed(mcs814x->regs + GPIO_PIN); 43 if (value) 44 mask |= (1 << offset); 45 else 46 mask &= ~(1 << offset); 47 writel_relaxed(mask, mcs814x->regs + GPIO_PIN); 48} 49 50static int mcs814x_gpio_direction_output(struct gpio_chip *chip, 51 unsigned offset, int value) 52{ 53 struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip); 54 u32 mask; 55 56 mask = readl_relaxed(mcs814x->regs + GPIO_DIR); 57 mask &= ~(1 << offset); 58 writel_relaxed(mask, mcs814x->regs + GPIO_DIR); 59 60 return 0; 61} 62 63static int mcs814x_gpio_direction_input(struct gpio_chip *chip, 64 unsigned offset) 65{ 66 struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip); 67 u32 mask; 68 69 mask = readl_relaxed(mcs814x->regs + GPIO_DIR); 70 mask |= (1 << offset); 71 writel_relaxed(mask, mcs814x->regs + GPIO_DIR); 72 73 return 0; 74} 75 76static int __devinit mcs814x_gpio_probe(struct platform_device *pdev) 77{ 78 struct resource *res; 79 struct mcs814x_gpio_chip *mcs814x_chip; 80 int ret; 81 const unsigned int *num_gpios; 82 83 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 84 if (!res) 85 return -ENODEV; 86 87 num_gpios = of_get_property(pdev->dev.of_node, "num-gpios", NULL); 88 if (!num_gpios) 89 dev_err(&pdev->dev, "FIXME: no num-gpios property\n"); 90 91 mcs814x_chip = kzalloc(sizeof(*mcs814x_chip), GFP_KERNEL); 92 if (!mcs814x_chip) 93 return -ENOMEM; 94 95 mcs814x_chip->regs = devm_request_and_ioremap(&pdev->dev, res); 96 if (!mcs814x_chip->regs) { 97 ret = -ENOMEM; 98 goto out; 99 } 100 101 platform_set_drvdata(pdev, mcs814x_chip); 102 103#ifdef CONFIG_OF_GPIO 104 mcs814x_chip->chip.of_node = pdev->dev.of_node; 105#endif 106 107 mcs814x_chip->chip.label = pdev->name; 108 mcs814x_chip->chip.get = mcs814x_gpio_get; 109 mcs814x_chip->chip.set = mcs814x_gpio_set; 110 mcs814x_chip->chip.direction_input = mcs814x_gpio_direction_input; 111 mcs814x_chip->chip.direction_output = mcs814x_gpio_direction_output; 112 mcs814x_chip->chip.ngpio = be32_to_cpup(num_gpios); 113 /* we want dynamic base allocation */ 114 mcs814x_chip->chip.base = -1; 115 116 ret = gpiochip_add(&mcs814x_chip->chip); 117 if (ret) { 118 dev_err(&pdev->dev, "failed to register gpiochip\n"); 119 goto out; 120 } 121 122 return 0; 123 124out: 125 platform_set_drvdata(pdev, NULL); 126 kfree(mcs814x_chip); 127 return ret; 128} 129 130static struct of_device_id mcs814x_gpio_ids[] __devinitdata = { 131 { .compatible = "moschip,mcs814x-gpio" }, 132 { /* sentinel */ }, 133}; 134 135static struct platform_driver mcs814x_gpio_driver = { 136 .driver = { 137 .name = "mcs814x-gpio", 138 .owner = THIS_MODULE, 139 .of_match_table = mcs814x_gpio_ids, 140 }, 141 .probe = mcs814x_gpio_probe, 142}; 143 144int __init mcs814x_gpio_init(void) 145{ 146 return platform_driver_register(&mcs814x_gpio_driver); 147} 148postcore_initcall(mcs814x_gpio_init); 149