| /* |
| * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 |
| * |
| * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> |
| * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as published by |
| * the Free Software Foundation |
| */ |
| |
| #include <linux/errno.h> |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/io.h> |
| #include <linux/ioport.h> |
| #include <linux/c2port.h> |
| |
| #define DATA_PORT 0x325 |
| #define DIR_PORT 0x326 |
| #define C2D (1 << 0) |
| #define C2CK (1 << 1) |
| |
| static DEFINE_MUTEX(update_lock); |
| |
| /* |
| * C2 port operations |
| */ |
| |
| static void duramar2150_c2port_access(struct c2port_device *dev, int status) |
| { |
| u8 v; |
| |
| mutex_lock(&update_lock); |
| |
| v = inb(DIR_PORT); |
| |
| /* 0 = input, 1 = output */ |
| if (status) |
| outb(v | (C2D | C2CK), DIR_PORT); |
| else |
| /* When access is "off" is important that both lines are set |
| * as inputs or hi-impedance */ |
| outb(v & ~(C2D | C2CK), DIR_PORT); |
| |
| mutex_unlock(&update_lock); |
| } |
| |
| static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) |
| { |
| u8 v; |
| |
| mutex_lock(&update_lock); |
| |
| v = inb(DIR_PORT); |
| |
| if (dir) |
| outb(v & ~C2D, DIR_PORT); |
| else |
| outb(v | C2D, DIR_PORT); |
| |
| mutex_unlock(&update_lock); |
| } |
| |
| static int duramar2150_c2port_c2d_get(struct c2port_device *dev) |
| { |
| return inb(DATA_PORT) & C2D; |
| } |
| |
| static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) |
| { |
| u8 v; |
| |
| mutex_lock(&update_lock); |
| |
| v = inb(DATA_PORT); |
| |
| if (status) |
| outb(v | C2D, DATA_PORT); |
| else |
| outb(v & ~C2D, DATA_PORT); |
| |
| mutex_unlock(&update_lock); |
| } |
| |
| static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) |
| { |
| u8 v; |
| |
| mutex_lock(&update_lock); |
| |
| v = inb(DATA_PORT); |
| |
| if (status) |
| outb(v | C2CK, DATA_PORT); |
| else |
| outb(v & ~C2CK, DATA_PORT); |
| |
| mutex_unlock(&update_lock); |
| } |
| |
| static struct c2port_ops duramar2150_c2port_ops = { |
| .block_size = 512, /* bytes */ |
| .blocks_num = 30, /* total flash size: 15360 bytes */ |
| |
| .access = duramar2150_c2port_access, |
| .c2d_dir = duramar2150_c2port_c2d_dir, |
| .c2d_get = duramar2150_c2port_c2d_get, |
| .c2d_set = duramar2150_c2port_c2d_set, |
| .c2ck_set = duramar2150_c2port_c2ck_set, |
| }; |
| |
| static struct c2port_device *duramar2150_c2port_dev; |
| |
| /* |
| * Module stuff |
| */ |
| |
| static int __init duramar2150_c2port_init(void) |
| { |
| struct resource *res; |
| int ret = 0; |
| |
| res = request_region(0x325, 2, "c2port"); |
| if (!res) |
| return -EBUSY; |
| |
| duramar2150_c2port_dev = c2port_device_register("uc", |
| &duramar2150_c2port_ops, NULL); |
| if (!duramar2150_c2port_dev) { |
| ret = -ENODEV; |
| goto free_region; |
| } |
| |
| return 0; |
| |
| free_region: |
| release_region(0x325, 2); |
| return ret; |
| } |
| |
| static void __exit duramar2150_c2port_exit(void) |
| { |
| /* Setup the GPIOs as input by default (access = 0) */ |
| duramar2150_c2port_access(duramar2150_c2port_dev, 0); |
| |
| c2port_device_unregister(duramar2150_c2port_dev); |
| |
| release_region(0x325, 2); |
| } |
| |
| module_init(duramar2150_c2port_init); |
| module_exit(duramar2150_c2port_exit); |
| |
| MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); |
| MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); |
| MODULE_LICENSE("GPL"); |