xref: /OK3568_Linux_fs/kernel/drivers/gpio/gpio-janz-ttl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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