165131cd5SRodolfo Giometti /*
265131cd5SRodolfo Giometti  *  Silicon Labs C2 port Linux support for Eurotech Duramar 2150
365131cd5SRodolfo Giometti  *
465131cd5SRodolfo Giometti  *  Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it>
565131cd5SRodolfo Giometti  *  Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it>
665131cd5SRodolfo Giometti  *
765131cd5SRodolfo Giometti  * This program is free software; you can redistribute it and/or modify it
865131cd5SRodolfo Giometti  * under the terms of the GNU General Public License version 2 as published by
965131cd5SRodolfo Giometti  * the Free Software Foundation
1065131cd5SRodolfo Giometti  */
1165131cd5SRodolfo Giometti 
1265131cd5SRodolfo Giometti #include <linux/errno.h>
1365131cd5SRodolfo Giometti #include <linux/init.h>
1465131cd5SRodolfo Giometti #include <linux/kernel.h>
1565131cd5SRodolfo Giometti #include <linux/module.h>
1665131cd5SRodolfo Giometti #include <linux/delay.h>
1765131cd5SRodolfo Giometti #include <linux/io.h>
18ecd9d34aSPaul Gortmaker #include <linux/ioport.h>
1965131cd5SRodolfo Giometti #include <linux/c2port.h>
2065131cd5SRodolfo Giometti 
2165131cd5SRodolfo Giometti #define DATA_PORT	0x325
2265131cd5SRodolfo Giometti #define DIR_PORT	0x326
2365131cd5SRodolfo Giometti #define    C2D		   (1 << 0)
2465131cd5SRodolfo Giometti #define    C2CK		   (1 << 1)
2565131cd5SRodolfo Giometti 
2665131cd5SRodolfo Giometti static DEFINE_MUTEX(update_lock);
2765131cd5SRodolfo Giometti 
2865131cd5SRodolfo Giometti /*
2965131cd5SRodolfo Giometti  * C2 port operations
3065131cd5SRodolfo Giometti  */
3165131cd5SRodolfo Giometti 
3265131cd5SRodolfo Giometti static void duramar2150_c2port_access(struct c2port_device *dev, int status)
3365131cd5SRodolfo Giometti {
3465131cd5SRodolfo Giometti 	u8 v;
3565131cd5SRodolfo Giometti 
3665131cd5SRodolfo Giometti 	mutex_lock(&update_lock);
3765131cd5SRodolfo Giometti 
3865131cd5SRodolfo Giometti 	v = inb(DIR_PORT);
3965131cd5SRodolfo Giometti 
4065131cd5SRodolfo Giometti 	/* 0 = input, 1 = output */
4165131cd5SRodolfo Giometti 	if (status)
4265131cd5SRodolfo Giometti 		outb(v | (C2D | C2CK), DIR_PORT);
4365131cd5SRodolfo Giometti 	else
4465131cd5SRodolfo Giometti 		/* When access is "off" is important that both lines are set
4525985edcSLucas De Marchi 		 * as inputs or hi-impedance */
4665131cd5SRodolfo Giometti 		outb(v & ~(C2D | C2CK), DIR_PORT);
4765131cd5SRodolfo Giometti 
4865131cd5SRodolfo Giometti 	mutex_unlock(&update_lock);
4965131cd5SRodolfo Giometti }
5065131cd5SRodolfo Giometti 
5165131cd5SRodolfo Giometti static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir)
5265131cd5SRodolfo Giometti {
5365131cd5SRodolfo Giometti 	u8 v;
5465131cd5SRodolfo Giometti 
5565131cd5SRodolfo Giometti 	mutex_lock(&update_lock);
5665131cd5SRodolfo Giometti 
5765131cd5SRodolfo Giometti 	v = inb(DIR_PORT);
5865131cd5SRodolfo Giometti 
5965131cd5SRodolfo Giometti 	if (dir)
6065131cd5SRodolfo Giometti 		outb(v & ~C2D, DIR_PORT);
6165131cd5SRodolfo Giometti 	else
6265131cd5SRodolfo Giometti 		outb(v | C2D, DIR_PORT);
6365131cd5SRodolfo Giometti 
6465131cd5SRodolfo Giometti 	mutex_unlock(&update_lock);
6565131cd5SRodolfo Giometti }
6665131cd5SRodolfo Giometti 
6765131cd5SRodolfo Giometti static int duramar2150_c2port_c2d_get(struct c2port_device *dev)
6865131cd5SRodolfo Giometti {
6965131cd5SRodolfo Giometti 	return inb(DATA_PORT) & C2D;
7065131cd5SRodolfo Giometti }
7165131cd5SRodolfo Giometti 
7265131cd5SRodolfo Giometti static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status)
7365131cd5SRodolfo Giometti {
7465131cd5SRodolfo Giometti 	u8 v;
7565131cd5SRodolfo Giometti 
7665131cd5SRodolfo Giometti 	mutex_lock(&update_lock);
7765131cd5SRodolfo Giometti 
7865131cd5SRodolfo Giometti 	v = inb(DATA_PORT);
7965131cd5SRodolfo Giometti 
8065131cd5SRodolfo Giometti 	if (status)
8165131cd5SRodolfo Giometti 		outb(v | C2D, DATA_PORT);
8265131cd5SRodolfo Giometti 	else
8365131cd5SRodolfo Giometti 		outb(v & ~C2D, DATA_PORT);
8465131cd5SRodolfo Giometti 
8565131cd5SRodolfo Giometti 	mutex_unlock(&update_lock);
8665131cd5SRodolfo Giometti }
8765131cd5SRodolfo Giometti 
8865131cd5SRodolfo Giometti static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status)
8965131cd5SRodolfo Giometti {
9065131cd5SRodolfo Giometti 	u8 v;
9165131cd5SRodolfo Giometti 
9265131cd5SRodolfo Giometti 	mutex_lock(&update_lock);
9365131cd5SRodolfo Giometti 
9465131cd5SRodolfo Giometti 	v = inb(DATA_PORT);
9565131cd5SRodolfo Giometti 
9665131cd5SRodolfo Giometti 	if (status)
9765131cd5SRodolfo Giometti 		outb(v | C2CK, DATA_PORT);
9865131cd5SRodolfo Giometti 	else
9965131cd5SRodolfo Giometti 		outb(v & ~C2CK, DATA_PORT);
10065131cd5SRodolfo Giometti 
10165131cd5SRodolfo Giometti 	mutex_unlock(&update_lock);
10265131cd5SRodolfo Giometti }
10365131cd5SRodolfo Giometti 
10465131cd5SRodolfo Giometti static struct c2port_ops duramar2150_c2port_ops = {
10565131cd5SRodolfo Giometti 	.block_size	= 512,	/* bytes */
10665131cd5SRodolfo Giometti 	.blocks_num	= 30,	/* total flash size: 15360 bytes */
10765131cd5SRodolfo Giometti 
10865131cd5SRodolfo Giometti 	.access		= duramar2150_c2port_access,
10965131cd5SRodolfo Giometti 	.c2d_dir	= duramar2150_c2port_c2d_dir,
11065131cd5SRodolfo Giometti 	.c2d_get	= duramar2150_c2port_c2d_get,
11165131cd5SRodolfo Giometti 	.c2d_set	= duramar2150_c2port_c2d_set,
11265131cd5SRodolfo Giometti 	.c2ck_set	= duramar2150_c2port_c2ck_set,
11365131cd5SRodolfo Giometti };
11465131cd5SRodolfo Giometti 
11565131cd5SRodolfo Giometti static struct c2port_device *duramar2150_c2port_dev;
11665131cd5SRodolfo Giometti 
11765131cd5SRodolfo Giometti /*
11865131cd5SRodolfo Giometti  * Module stuff
11965131cd5SRodolfo Giometti  */
12065131cd5SRodolfo Giometti 
12165131cd5SRodolfo Giometti static int __init duramar2150_c2port_init(void)
12265131cd5SRodolfo Giometti {
12365131cd5SRodolfo Giometti 	struct resource *res;
12465131cd5SRodolfo Giometti 	int ret = 0;
12565131cd5SRodolfo Giometti 
12665131cd5SRodolfo Giometti 	res = request_region(0x325, 2, "c2port");
12765131cd5SRodolfo Giometti 	if (!res)
12865131cd5SRodolfo Giometti 		return -EBUSY;
12965131cd5SRodolfo Giometti 
13065131cd5SRodolfo Giometti 	duramar2150_c2port_dev = c2port_device_register("uc",
13165131cd5SRodolfo Giometti 					&duramar2150_c2port_ops, NULL);
1328128a31eSDan Carpenter 	if (IS_ERR(duramar2150_c2port_dev)) {
1338128a31eSDan Carpenter 		ret = PTR_ERR(duramar2150_c2port_dev);
13465131cd5SRodolfo Giometti 		goto free_region;
13565131cd5SRodolfo Giometti 	}
13665131cd5SRodolfo Giometti 
13765131cd5SRodolfo Giometti 	return 0;
13865131cd5SRodolfo Giometti 
13965131cd5SRodolfo Giometti free_region:
14065131cd5SRodolfo Giometti 	release_region(0x325, 2);
14165131cd5SRodolfo Giometti 	return ret;
14265131cd5SRodolfo Giometti }
14365131cd5SRodolfo Giometti 
14465131cd5SRodolfo Giometti static void __exit duramar2150_c2port_exit(void)
14565131cd5SRodolfo Giometti {
14665131cd5SRodolfo Giometti 	/* Setup the GPIOs as input by default (access = 0) */
14765131cd5SRodolfo Giometti 	duramar2150_c2port_access(duramar2150_c2port_dev, 0);
14865131cd5SRodolfo Giometti 
14965131cd5SRodolfo Giometti 	c2port_device_unregister(duramar2150_c2port_dev);
15065131cd5SRodolfo Giometti 
15165131cd5SRodolfo Giometti 	release_region(0x325, 2);
15265131cd5SRodolfo Giometti }
15365131cd5SRodolfo Giometti 
15465131cd5SRodolfo Giometti module_init(duramar2150_c2port_init);
15565131cd5SRodolfo Giometti module_exit(duramar2150_c2port_exit);
15665131cd5SRodolfo Giometti 
15765131cd5SRodolfo Giometti MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
15865131cd5SRodolfo Giometti MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150");
15965131cd5SRodolfo Giometti MODULE_LICENSE("GPL");
160