1/* 2 * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 3 * 4 * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> 5 * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation 10 */ 11 12#include <linux/errno.h> 13#include <linux/init.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/delay.h> 17#include <linux/io.h> 18#include <linux/c2port.h> 19 20#define DATA_PORT 0x325 21#define DIR_PORT 0x326 22#define C2D (1 << 0) 23#define C2CK (1 << 1) 24 25static DEFINE_MUTEX(update_lock); 26 27/* 28 * C2 port operations 29 */ 30 31static void duramar2150_c2port_access(struct c2port_device *dev, int status) 32{ 33 u8 v; 34 35 mutex_lock(&update_lock); 36 37 v = inb(DIR_PORT); 38 39 /* 0 = input, 1 = output */ 40 if (status) 41 outb(v | (C2D | C2CK), DIR_PORT); 42 else 43 /* When access is "off" is important that both lines are set 44 * as inputs or hi-impedence */ 45 outb(v & ~(C2D | C2CK), DIR_PORT); 46 47 mutex_unlock(&update_lock); 48} 49 50static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) 51{ 52 u8 v; 53 54 mutex_lock(&update_lock); 55 56 v = inb(DIR_PORT); 57 58 if (dir) 59 outb(v & ~C2D, DIR_PORT); 60 else 61 outb(v | C2D, DIR_PORT); 62 63 mutex_unlock(&update_lock); 64} 65 66static int duramar2150_c2port_c2d_get(struct c2port_device *dev) 67{ 68 return inb(DATA_PORT) & C2D; 69} 70 71static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) 72{ 73 u8 v; 74 75 mutex_lock(&update_lock); 76 77 v = inb(DATA_PORT); 78 79 if (status) 80 outb(v | C2D, DATA_PORT); 81 else 82 outb(v & ~C2D, DATA_PORT); 83 84 mutex_unlock(&update_lock); 85} 86 87static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) 88{ 89 u8 v; 90 91 mutex_lock(&update_lock); 92 93 v = inb(DATA_PORT); 94 95 if (status) 96 outb(v | C2CK, DATA_PORT); 97 else 98 outb(v & ~C2CK, DATA_PORT); 99 100 mutex_unlock(&update_lock); 101} 102 103static struct c2port_ops duramar2150_c2port_ops = { 104 .block_size = 512, /* bytes */ 105 .blocks_num = 30, /* total flash size: 15360 bytes */ 106 107 .access = duramar2150_c2port_access, 108 .c2d_dir = duramar2150_c2port_c2d_dir, 109 .c2d_get = duramar2150_c2port_c2d_get, 110 .c2d_set = duramar2150_c2port_c2d_set, 111 .c2ck_set = duramar2150_c2port_c2ck_set, 112}; 113 114static struct c2port_device *duramar2150_c2port_dev; 115 116/* 117 * Module stuff 118 */ 119 120static int __init duramar2150_c2port_init(void) 121{ 122 struct resource *res; 123 int ret = 0; 124 125 res = request_region(0x325, 2, "c2port"); 126 if (!res) 127 return -EBUSY; 128 129 duramar2150_c2port_dev = c2port_device_register("uc", 130 &duramar2150_c2port_ops, NULL); 131 if (!duramar2150_c2port_dev) { 132 ret = -ENODEV; 133 goto free_region; 134 } 135 136 return 0; 137 138free_region: 139 release_region(0x325, 2); 140 return ret; 141} 142 143static void __exit duramar2150_c2port_exit(void) 144{ 145 /* Setup the GPIOs as input by default (access = 0) */ 146 duramar2150_c2port_access(duramar2150_c2port_dev, 0); 147 148 c2port_device_unregister(duramar2150_c2port_dev); 149 150 release_region(0x325, 2); 151} 152 153module_init(duramar2150_c2port_init); 154module_exit(duramar2150_c2port_exit); 155 156MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 157MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); 158MODULE_LICENSE("GPL"); 159