201 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Janz MODULbus VMOD-TTL GPIO Driver4 *5 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>6 */7 8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/init.h>11#include <linux/interrupt.h>12#include <linux/delay.h>13#include <linux/platform_device.h>14#include <linux/io.h>15#include <linux/gpio/driver.h>16#include <linux/slab.h>17#include <linux/bitops.h>18 19#include <linux/mfd/janz.h>20 21#define DRV_NAME "janz-ttl"22 23#define PORTA_DIRECTION 0x2324#define PORTB_DIRECTION 0x2B25#define PORTC_DIRECTION 0x0626#define PORTA_IOCTL 0x2427#define PORTB_IOCTL 0x2C28#define PORTC_IOCTL 0x0729 30#define MASTER_INT_CTL 0x0031#define MASTER_CONF_CTL 0x0132 33#define CONF_PAE BIT(2)34#define CONF_PBE BIT(7)35#define CONF_PCE BIT(4)36 37struct ttl_control_regs {38 __be16 portc;39 __be16 portb;40 __be16 porta;41 __be16 control;42};43 44struct ttl_module {45 struct gpio_chip gpio;46 47 /* base address of registers */48 struct ttl_control_regs __iomem *regs;49 50 u8 portc_shadow;51 u8 portb_shadow;52 u8 porta_shadow;53 54 spinlock_t lock;55};56 57static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)58{59 struct ttl_module *mod = dev_get_drvdata(gpio->parent);60 u8 *shadow;61 int ret;62 63 if (offset < 8) {64 shadow = &mod->porta_shadow;65 } else if (offset < 16) {66 shadow = &mod->portb_shadow;67 offset -= 8;68 } else {69 shadow = &mod->portc_shadow;70 offset -= 16;71 }72 73 spin_lock(&mod->lock);74 ret = *shadow & BIT(offset);75 spin_unlock(&mod->lock);76 return !!ret;77}78 79static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)80{81 struct ttl_module *mod = dev_get_drvdata(gpio->parent);82 void __iomem *port;83 u8 *shadow;84 85 if (offset < 8) {86 port = &mod->regs->porta;87 shadow = &mod->porta_shadow;88 } else if (offset < 16) {89 port = &mod->regs->portb;90 shadow = &mod->portb_shadow;91 offset -= 8;92 } else {93 port = &mod->regs->portc;94 shadow = &mod->portc_shadow;95 offset -= 16;96 }97 98 spin_lock(&mod->lock);99 if (value)100 *shadow |= BIT(offset);101 else102 *shadow &= ~BIT(offset);103 104 iowrite16be(*shadow, port);105 spin_unlock(&mod->lock);106}107 108static void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)109{110 iowrite16be(reg, &mod->regs->control);111 iowrite16be(val, &mod->regs->control);112}113 114static void ttl_setup_device(struct ttl_module *mod)115{116 /* reset the device to a known state */117 iowrite16be(0x0000, &mod->regs->control);118 iowrite16be(0x0001, &mod->regs->control);119 iowrite16be(0x0000, &mod->regs->control);120 121 /* put all ports in open-drain mode */122 ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);123 ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);124 ttl_write_reg(mod, PORTC_IOCTL, 0x000f);125 126 /* set all ports as outputs */127 ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);128 ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);129 ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);130 131 /* set all ports to drive zeroes */132 iowrite16be(0x0000, &mod->regs->porta);133 iowrite16be(0x0000, &mod->regs->portb);134 iowrite16be(0x0000, &mod->regs->portc);135 136 /* enable all ports */137 ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);138}139 140static int ttl_probe(struct platform_device *pdev)141{142 struct janz_platform_data *pdata;143 struct ttl_module *mod;144 struct gpio_chip *gpio;145 int ret;146 147 pdata = dev_get_platdata(&pdev->dev);148 if (!pdata) {149 dev_err(&pdev->dev, "no platform data\n");150 return -ENXIO;151 }152 153 mod = devm_kzalloc(&pdev->dev, sizeof(*mod), GFP_KERNEL);154 if (!mod)155 return -ENOMEM;156 157 platform_set_drvdata(pdev, mod);158 spin_lock_init(&mod->lock);159 160 /* get access to the MODULbus registers for this module */161 mod->regs = devm_platform_ioremap_resource(pdev, 0);162 if (IS_ERR(mod->regs))163 return PTR_ERR(mod->regs);164 165 ttl_setup_device(mod);166 167 /* Initialize the GPIO data structures */168 gpio = &mod->gpio;169 gpio->parent = &pdev->dev;170 gpio->label = pdev->name;171 gpio->get = ttl_get_value;172 gpio->set = ttl_set_value;173 gpio->owner = THIS_MODULE;174 175 /* request dynamic allocation */176 gpio->base = -1;177 gpio->ngpio = 20;178 179 ret = devm_gpiochip_add_data(&pdev->dev, gpio, NULL);180 if (ret) {181 dev_err(&pdev->dev, "unable to add GPIO chip\n");182 return ret;183 }184 185 return 0;186}187 188static struct platform_driver ttl_driver = {189 .driver = {190 .name = DRV_NAME,191 },192 .probe = ttl_probe,193};194 195module_platform_driver(ttl_driver);196 197MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");198MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");199MODULE_LICENSE("GPL");200MODULE_ALIAS("platform:janz-ttl");201