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