159024Sobrien// SPDX-License-Identifier: GPL-2.0-or-later 278828Sobrien/* 3218822Sdim * Janz MODULbus VMOD-TTL GPIO Driver 459024Sobrien * 559024Sobrien * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> 6218822Sdim */ 759024Sobrien 8218822Sdim#include <linux/kernel.h> 9218822Sdim#include <linux/module.h> 10218822Sdim#include <linux/init.h> 11218822Sdim#include <linux/interrupt.h> 1259024Sobrien#include <linux/delay.h> 13218822Sdim#include <linux/platform_device.h> 14218822Sdim#include <linux/io.h> 15218822Sdim#include <linux/gpio/driver.h> 16218822Sdim#include <linux/slab.h> 1759024Sobrien#include <linux/bitops.h> 18218822Sdim 19218822Sdim#include <linux/mfd/janz.h> 20218822Sdim 21218822Sdim#define DRV_NAME "janz-ttl" 2259024Sobrien 2359024Sobrien#define PORTA_DIRECTION 0x23 2459024Sobrien#define PORTB_DIRECTION 0x2B 2559024Sobrien#define PORTC_DIRECTION 0x06 2659024Sobrien#define PORTA_IOCTL 0x24 2759024Sobrien#define PORTB_IOCTL 0x2C 2860484Sobrien#define PORTC_IOCTL 0x07 2959024Sobrien 3059024Sobrien#define MASTER_INT_CTL 0x00 3159024Sobrien#define MASTER_CONF_CTL 0x01 3259024Sobrien 3359024Sobrien#define CONF_PAE BIT(2) 3459024Sobrien#define CONF_PBE BIT(7) 3559024Sobrien#define CONF_PCE BIT(4) 3659024Sobrien 3759024Sobrienstruct ttl_control_regs { 3859024Sobrien __be16 portc; 3977298Sobrien __be16 portb; 4059024Sobrien __be16 porta; 4159024Sobrien __be16 control; 4259024Sobrien}; 4359024Sobrien 4477298Sobrienstruct ttl_module { 45218822Sdim struct gpio_chip gpio; 4659024Sobrien 4777298Sobrien /* base address of registers */ 4859024Sobrien struct ttl_control_regs __iomem *regs; 4977298Sobrien 5059024Sobrien u8 portc_shadow; 5159024Sobrien u8 portb_shadow; 5259024Sobrien u8 porta_shadow; 5359024Sobrien 5459024Sobrien spinlock_t lock; 5559024Sobrien}; 5677298Sobrien 5777298Sobrienstatic int ttl_get_value(struct gpio_chip *gpio, unsigned offset) 5859024Sobrien{ 5959024Sobrien struct ttl_module *mod = dev_get_drvdata(gpio->parent); 6077298Sobrien u8 *shadow; 6177298Sobrien int ret; 6277298Sobrien 63218822Sdim if (offset < 8) { 6459024Sobrien shadow = &mod->porta_shadow; 6559024Sobrien } else if (offset < 16) { 6659024Sobrien shadow = &mod->portb_shadow; 67218822Sdim offset -= 8; 6859024Sobrien } else { 6959024Sobrien shadow = &mod->portc_shadow; 7059024Sobrien offset -= 16; 7159024Sobrien } 7259024Sobrien 73218822Sdim spin_lock(&mod->lock); 74218822Sdim ret = *shadow & BIT(offset); 7559024Sobrien spin_unlock(&mod->lock); 7659024Sobrien return !!ret; 7759024Sobrien} 7859024Sobrien 7959024Sobrienstatic void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) 8059024Sobrien{ 8159024Sobrien struct ttl_module *mod = dev_get_drvdata(gpio->parent); 8259024Sobrien void __iomem *port; 8359024Sobrien u8 *shadow; 8477298Sobrien 8577298Sobrien if (offset < 8) { 8659024Sobrien port = &mod->regs->porta; 8759024Sobrien shadow = &mod->porta_shadow; 8859024Sobrien } else if (offset < 16) { 8959024Sobrien port = &mod->regs->portb; 9059024Sobrien shadow = &mod->portb_shadow; 9159024Sobrien offset -= 8; 92218822Sdim } else { 9359024Sobrien port = &mod->regs->portc; 9459024Sobrien shadow = &mod->portc_shadow; 9559024Sobrien offset -= 16; 9659024Sobrien } 97218822Sdim 98218822Sdim spin_lock(&mod->lock); 9959024Sobrien if (value) 10059024Sobrien *shadow |= BIT(offset); 10159024Sobrien else 10259024Sobrien *shadow &= ~BIT(offset); 10359024Sobrien 104218822Sdim iowrite16be(*shadow, port); 10559024Sobrien spin_unlock(&mod->lock); 10659024Sobrien} 107218822Sdim 10859024Sobrienstatic void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) 10959024Sobrien{ 110218822Sdim iowrite16be(reg, &mod->regs->control); 11159024Sobrien iowrite16be(val, &mod->regs->control); 112218822Sdim} 113218822Sdim 114218822Sdimstatic void ttl_setup_device(struct ttl_module *mod) 115218822Sdim{ 116218822Sdim /* reset the device to a known state */ 117218822Sdim iowrite16be(0x0000, &mod->regs->control); 118218822Sdim iowrite16be(0x0001, &mod->regs->control); 119218822Sdim iowrite16be(0x0000, &mod->regs->control); 120218822Sdim 121218822Sdim /* put all ports in open-drain mode */ 122218822Sdim ttl_write_reg(mod, PORTA_IOCTL, 0x00ff); 123218822Sdim ttl_write_reg(mod, PORTB_IOCTL, 0x00ff); 124218822Sdim ttl_write_reg(mod, PORTC_IOCTL, 0x000f); 125218822Sdim 126218822Sdim /* set all ports as outputs */ 127218822Sdim ttl_write_reg(mod, PORTA_DIRECTION, 0x0000); 12859024Sobrien ttl_write_reg(mod, PORTB_DIRECTION, 0x0000); 12959024Sobrien ttl_write_reg(mod, PORTC_DIRECTION, 0x0000); 13059024Sobrien 13159024Sobrien /* set all ports to drive zeroes */ 13259024Sobrien iowrite16be(0x0000, &mod->regs->porta); 13359024Sobrien iowrite16be(0x0000, &mod->regs->portb); 134218822Sdim iowrite16be(0x0000, &mod->regs->portc); 135218822Sdim 136218822Sdim /* enable all ports */ 137218822Sdim ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE); 138218822Sdim} 139218822Sdim 140218822Sdimstatic int ttl_probe(struct platform_device *pdev) 141218822Sdim{ 142218822Sdim struct janz_platform_data *pdata; 143218822Sdim struct ttl_module *mod; 144218822Sdim struct gpio_chip *gpio; 145218822Sdim int ret; 146218822Sdim 147218822Sdim pdata = dev_get_platdata(&pdev->dev); 148218822Sdim if (!pdata) { 149218822Sdim dev_err(&pdev->dev, "no platform data\n"); 15059024Sobrien return -ENXIO; 15159024Sobrien } 15259024Sobrien 15359024Sobrien mod = devm_kzalloc(&pdev->dev, sizeof(*mod), GFP_KERNEL); 154218822Sdim if (!mod) 155218822Sdim return -ENOMEM; 156218822Sdim 157218822Sdim platform_set_drvdata(pdev, mod); 15859024Sobrien spin_lock_init(&mod->lock); 15959024Sobrien 160218822Sdim /* get access to the MODULbus registers for this module */ 16159024Sobrien mod->regs = devm_platform_ioremap_resource(pdev, 0); 16259024Sobrien if (IS_ERR(mod->regs)) 16359024Sobrien return PTR_ERR(mod->regs); 164218822Sdim 16559024Sobrien ttl_setup_device(mod); 16659024Sobrien 16759024Sobrien /* Initialize the GPIO data structures */ 16859024Sobrien gpio = &mod->gpio; 16959024Sobrien gpio->parent = &pdev->dev; 17059024Sobrien gpio->label = pdev->name; 17159024Sobrien gpio->get = ttl_get_value; 17259024Sobrien gpio->set = ttl_set_value; 17359024Sobrien gpio->owner = THIS_MODULE; 17459024Sobrien 17559024Sobrien /* request dynamic allocation */ 17659024Sobrien gpio->base = -1; 17759024Sobrien gpio->ngpio = 20; 17859024Sobrien 17959024Sobrien ret = devm_gpiochip_add_data(&pdev->dev, gpio, NULL); 18059024Sobrien if (ret) { 18159024Sobrien dev_err(&pdev->dev, "unable to add GPIO chip\n"); 18259024Sobrien return ret; 18359024Sobrien } 18459024Sobrien 18559024Sobrien return 0; 18659024Sobrien} 18759024Sobrien 18859024Sobrienstatic struct platform_driver ttl_driver = { 18959024Sobrien .driver = { 19059024Sobrien .name = DRV_NAME, 19159024Sobrien }, 19259024Sobrien .probe = ttl_probe, 19359024Sobrien}; 19459024Sobrien 19559024Sobrienmodule_platform_driver(ttl_driver); 19659024Sobrien 19759024SobrienMODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); 19859024SobrienMODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver"); 19959024SobrienMODULE_LICENSE("GPL"); 20059024SobrienMODULE_ALIAS("platform:janz-ttl"); 20159024Sobrien