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