/*************************************************************************** * Copyright (C) 2013 Bruno Ferreira * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #define DRVNAME "f81865_gpio" /* Global Control Registers */ #define SIO_F81865_LD_WDT 0x07 /* Watchdog Logic Number Register (LDN) */ #define SIO_F81865_LD_GPIO 0x06 /* GPIO Logic Number Register (LDN) */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ #define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ #define SIO_REG_DEVREV 0x22 /* Device revision */ #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ /* Manufacture and Chip Information */ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID*/ static DEFINE_SPINLOCK(gpio_lock); /* GPIO internal data information */ struct gpio_data { unsigned short sioaddr; /* default index port */ unsigned long opened; /* driver open state */ }; static struct gpio_data gpio = {}; /* Super-I/O Function prototypes */ static inline int superio_enter(int base); static inline void superio_exit(int base); static inline int superio_inb(int base, int reg); static inline void superio_outb(int base, int reg, u8 val); static inline int superio_inw(int base, int reg); static inline void superio_set_bit(int base, int reg, int bit); static inline void superio_clear_bit(int base, int reg, int bit); /* Super I/O functions */ static inline int superio_enter(int base) { /* don't step on other drivers' I/O space by accident */ if (!request_muxed_region(base, 2, DRVNAME)) { printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n", (int)base); return -EBUSY; } /* according to the datasheet the key must be send twice! */ outb(SIO_UNLOCK_KEY, base); outb(SIO_UNLOCK_KEY, base); return 0; } static inline void superio_exit(int base) { outb(SIO_LOCK_KEY, base); release_region(base, 2); } static inline int superio_inb(int base, int reg) { outb(reg, base); return inb(base + 1); } static inline void superio_outb(int base, int reg, u8 val) { outb(reg, base); outb(val, base + 1); } static int superio_inw(int base, int reg) { int val; val = superio_inb(base, reg) << 8; val |= superio_inb(base, reg + 1); return val; } static inline void superio_set_bit(int base, int reg, int bit) { unsigned long val = superio_inb(base, reg); __set_bit(bit, &val); superio_outb(base, reg, val); } static inline void superio_clear_bit(int base, int reg, int bit) { unsigned long val = superio_inb(base, reg); __clear_bit(bit, &val); superio_outb(base, reg, val); } /* GPIO api */ static int f81865_gpio_direction_in(struct gpio_chip *gc, unsigned _gpio_num) { return 0; } static int f81865_gpio_direction_out(struct gpio_chip *gc, unsigned _gpio_num, int val) { return 0; } static int f81865_gpio_get(struct gpio_chip *gc, unsigned _gpio_num) { return 0; } static void f81865_gpio_set(struct gpio_chip *gc, unsigned _gpio_num, int val) { } static struct gpio_chip f81865_gpio_chip = { .label = DRVNAME, .owner = THIS_MODULE, .get = f81865_gpio_get, .direction_input = f81865_gpio_direction_in, .set = f81865_gpio_set, .direction_output = f81865_gpio_direction_out, }; /* Driver useful functions */ static int __init f81865_configure(void) { int err = 0; /* temporary */ //u8 io_reg, curr_vals; /* Enable all pins with GPIO capability */ /* By default we enable all possible GPIOs on the chip */ spin_lock(&gpio_lock); err = superio_enter(gpio.sioaddr); if (err) goto exit_unlock; /* Enable GPIO... */ //io_reg = 0x27; /* Enable Rom Address Register */ //curr_vals = superio_inb(gpio.sioaddr, io_reg); //printk(KERN_INFO DRVNAME ": Rom Address Register: (%d) \n", curr_vals); //superio_set_bit(gpio.sioaddr, io_reg, 4); /* PORT_4E_EN to 1*/ /* Enable GPIO[10-17], GPIO1 | WARNING the GPIO15 could be the WDTRST# */ /* GPIO1 Enable Register - Index 2Bh */ superio_outb(gpio.sioaddr, 0x2b, 0xff); /* Enable GPIO[20-27], GPIO1 */ /* GPIO2 Enable Register - Index 2Ch */ superio_outb(gpio.sioaddr, 0x2c, 0xff); /* Enable GPIO[30-37], GPIO1 */ /* GPIO3 Enable Register - Index 29h */ superio_outb(gpio.sioaddr, 0x29, 0xff); /* Enable GPIO[40-47], GPIO1 */ /* GPIO4 Enable Register - Index 28h */ superio_outb(gpio.sioaddr, 0x28, 0xff); superio_exit(gpio.sioaddr); exit_unlock: spin_unlock(&gpio_lock); printk(KERN_INFO DRVNAME ": configured(%d)...\n", err); return err; } static int __init f81865_find(int sio_addr) { u16 devid; int err = superio_enter(sio_addr); if (err) return err; devid = superio_inw(sio_addr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) { pr_debug(DRVNAME ": Not a Fintek device\n"); err = -ENODEV; goto exit; } devid = superio_inw(sio_addr, SIO_REG_DEVID); if (devid != SIO_F81865_ID) { printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n", (unsigned int) devid); err = -ENODEV; goto exit; } printk(KERN_INFO DRVNAME ": Found F81865 Super I/O chip, revision %d\n", (int) superio_inb(sio_addr, SIO_REG_DEVREV)); exit: superio_exit(sio_addr); return err; } static int __init f81865_gpio_init(void) { static const unsigned short addrs[] = { 0x2e, 0x4e }; int err = -ENODEV, i; for (i=0; i < ARRAY_SIZE(addrs); i++) { err = f81865_find(addrs[i]); if (err == 0) break; } if (i == ARRAY_SIZE(addrs)) return err; /* set GPIO base address */ gpio.sioaddr = addrs[i]; /* configure GPIO pins capability */ err = f81865_configure(); if (err) { printk(KERN_ERR DRVNAME ": configuring the GPIO...\n"); return EAGAIN; } f81865_gpio_chip.base = -1; f81865_gpio_chip.ngpio = 26; /* !!! FIX THIS !!! */ err = gpiochip_add(&f81865_gpio_chip); return (err < 0) ? err : 0; } static void __exit f81865_gpio_exit(void) { gpiochip_remove(&f81865_gpio_chip); printk(KERN_INFO DRVNAME ": unloaded...\n"); } MODULE_AUTHOR("Bruno Ferreira"); MODULE_DESCRIPTION("GPIO interface for F81865 Super I/O chip"); MODULE_LICENSE("GPL"); module_init(f81865_gpio_init); module_exit(f81865_gpio_exit);