162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Silicon Labs C2 port Linux support for Eurotech Duramar 2150
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it>
662306a36Sopenharmony_ci *  Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/ioport.h>
1662306a36Sopenharmony_ci#include <linux/c2port.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define DATA_PORT	0x325
1962306a36Sopenharmony_ci#define DIR_PORT	0x326
2062306a36Sopenharmony_ci#define    C2D		   (1 << 0)
2162306a36Sopenharmony_ci#define    C2CK		   (1 << 1)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic DEFINE_MUTEX(update_lock);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * C2 port operations
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void duramar2150_c2port_access(struct c2port_device *dev, int status)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	u8 v;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	mutex_lock(&update_lock);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	v = inb(DIR_PORT);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* 0 = input, 1 = output */
3862306a36Sopenharmony_ci	if (status)
3962306a36Sopenharmony_ci		outb(v | (C2D | C2CK), DIR_PORT);
4062306a36Sopenharmony_ci	else
4162306a36Sopenharmony_ci		/* When access is "off" is important that both lines are set
4262306a36Sopenharmony_ci		 * as inputs or hi-impedance */
4362306a36Sopenharmony_ci		outb(v & ~(C2D | C2CK), DIR_PORT);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	mutex_unlock(&update_lock);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u8 v;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	mutex_lock(&update_lock);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	v = inb(DIR_PORT);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (dir)
5762306a36Sopenharmony_ci		outb(v & ~C2D, DIR_PORT);
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		outb(v | C2D, DIR_PORT);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	mutex_unlock(&update_lock);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int duramar2150_c2port_c2d_get(struct c2port_device *dev)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return inb(DATA_PORT) & C2D;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	u8 v;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mutex_lock(&update_lock);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	v = inb(DATA_PORT);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (status)
7862306a36Sopenharmony_ci		outb(v | C2D, DATA_PORT);
7962306a36Sopenharmony_ci	else
8062306a36Sopenharmony_ci		outb(v & ~C2D, DATA_PORT);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	mutex_unlock(&update_lock);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	u8 v;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	mutex_lock(&update_lock);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	v = inb(DATA_PORT);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (status)
9462306a36Sopenharmony_ci		outb(v | C2CK, DATA_PORT);
9562306a36Sopenharmony_ci	else
9662306a36Sopenharmony_ci		outb(v & ~C2CK, DATA_PORT);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	mutex_unlock(&update_lock);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic struct c2port_ops duramar2150_c2port_ops = {
10262306a36Sopenharmony_ci	.block_size	= 512,	/* bytes */
10362306a36Sopenharmony_ci	.blocks_num	= 30,	/* total flash size: 15360 bytes */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	.access		= duramar2150_c2port_access,
10662306a36Sopenharmony_ci	.c2d_dir	= duramar2150_c2port_c2d_dir,
10762306a36Sopenharmony_ci	.c2d_get	= duramar2150_c2port_c2d_get,
10862306a36Sopenharmony_ci	.c2d_set	= duramar2150_c2port_c2d_set,
10962306a36Sopenharmony_ci	.c2ck_set	= duramar2150_c2port_c2ck_set,
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic struct c2port_device *duramar2150_c2port_dev;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*
11562306a36Sopenharmony_ci * Module stuff
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int __init duramar2150_c2port_init(void)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct resource *res;
12162306a36Sopenharmony_ci	int ret = 0;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	res = request_region(0x325, 2, "c2port");
12462306a36Sopenharmony_ci	if (!res)
12562306a36Sopenharmony_ci		return -EBUSY;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	duramar2150_c2port_dev = c2port_device_register("uc",
12862306a36Sopenharmony_ci					&duramar2150_c2port_ops, NULL);
12962306a36Sopenharmony_ci	if (IS_ERR(duramar2150_c2port_dev)) {
13062306a36Sopenharmony_ci		ret = PTR_ERR(duramar2150_c2port_dev);
13162306a36Sopenharmony_ci		goto free_region;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cifree_region:
13762306a36Sopenharmony_ci	release_region(0x325, 2);
13862306a36Sopenharmony_ci	return ret;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void __exit duramar2150_c2port_exit(void)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	/* Setup the GPIOs as input by default (access = 0) */
14462306a36Sopenharmony_ci	duramar2150_c2port_access(duramar2150_c2port_dev, 0);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	c2port_device_unregister(duramar2150_c2port_dev);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	release_region(0x325, 2);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cimodule_init(duramar2150_c2port_init);
15262306a36Sopenharmony_cimodule_exit(duramar2150_c2port_exit);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciMODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
15562306a36Sopenharmony_ciMODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150");
15662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
157