1/* arch/arm/plat-samsung/gpiolib.c 2 * 3 * Copyright 2008 Openmoko, Inc. 4 * Copyright 2008 Simtec Electronics 5 * Ben Dooks <ben@simtec.co.uk> 6 * http://armlinux.simtec.co.uk/ 7 * 8 * Copyright (c) 2009 Samsung Electronics Co., Ltd. 9 * http://www.samsung.com/ 10 * 11 * SAMSUNG - GPIOlib support 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License version 2 as 15 * published by the Free Software Foundation. 16 */ 17 18#include <linux/kernel.h> 19#include <linux/irq.h> 20#include <linux/io.h> 21#include <linux/gpio.h> 22#include <plat/gpio-core.h> 23#include <plat/gpio-cfg.h> 24#include <plat/gpio-cfg-helpers.h> 25 26#ifndef DEBUG_GPIO 27#define gpio_dbg(x...) do { } while (0) 28#else 29#define gpio_dbg(x...) printk(KERN_DEBUG x) 30#endif 31 32/* The samsung_gpiolib_4bit routines are to control the gpio banks where 33 * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the 34 * following example: 35 * 36 * base + 0x00: Control register, 4 bits per gpio 37 * gpio n: 4 bits starting at (4*n) 38 * 0000 = input, 0001 = output, others mean special-function 39 * base + 0x04: Data register, 1 bit per gpio 40 * bit n: data bit n 41 * 42 * Note, since the data register is one bit per gpio and is at base + 0x4 43 * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of 44 * the output. 45*/ 46 47static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, 48 unsigned int offset) 49{ 50 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); 51 void __iomem *base = ourchip->base; 52 unsigned long con; 53 54 con = __raw_readl(base + GPIOCON_OFF); 55 con &= ~(0xf << con_4bit_shift(offset)); 56 __raw_writel(con, base + GPIOCON_OFF); 57 58 gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); 59 60 return 0; 61} 62 63static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, 64 unsigned int offset, int value) 65{ 66 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); 67 void __iomem *base = ourchip->base; 68 unsigned long con; 69 unsigned long dat; 70 71 con = __raw_readl(base + GPIOCON_OFF); 72 con &= ~(0xf << con_4bit_shift(offset)); 73 con |= 0x1 << con_4bit_shift(offset); 74 75 dat = __raw_readl(base + GPIODAT_OFF); 76 77 if (value) 78 dat |= 1 << offset; 79 else 80 dat &= ~(1 << offset); 81 82 __raw_writel(dat, base + GPIODAT_OFF); 83 __raw_writel(con, base + GPIOCON_OFF); 84 __raw_writel(dat, base + GPIODAT_OFF); 85 86 gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); 87 88 return 0; 89} 90 91/* The next set of routines are for the case where the GPIO configuration 92 * registers are 4 bits per GPIO but there is more than one register (the 93 * bank has more than 8 GPIOs. 94 * 95 * This case is the similar to the 4 bit case, but the registers are as 96 * follows: 97 * 98 * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) 99 * gpio n: 4 bits starting at (4*n) 100 * 0000 = input, 0001 = output, others mean special-function 101 * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) 102 * gpio n: 4 bits starting at (4*n) 103 * 0000 = input, 0001 = output, others mean special-function 104 * base + 0x08: Data register, 1 bit per gpio 105 * bit n: data bit n 106 * 107 * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we 108 * store the 'base + 0x4' address so that these routines see the data 109 * register at ourchip->base + 0x04. 110 */ 111 112static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, 113 unsigned int offset) 114{ 115 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); 116 void __iomem *base = ourchip->base; 117 void __iomem *regcon = base; 118 unsigned long con; 119 120 if (offset > 7) 121 offset -= 8; 122 else 123 regcon -= 4; 124 125 con = __raw_readl(regcon); 126 con &= ~(0xf << con_4bit_shift(offset)); 127 __raw_writel(con, regcon); 128 129 gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); 130 131 return 0; 132} 133 134static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, 135 unsigned int offset, int value) 136{ 137 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); 138 void __iomem *base = ourchip->base; 139 void __iomem *regcon = base; 140 unsigned long con; 141 unsigned long dat; 142 unsigned con_offset = offset; 143 144 if (con_offset > 7) 145 con_offset -= 8; 146 else 147 regcon -= 4; 148 149 con = __raw_readl(regcon); 150 con &= ~(0xf << con_4bit_shift(con_offset)); 151 con |= 0x1 << con_4bit_shift(con_offset); 152 153 dat = __raw_readl(base + GPIODAT_OFF); 154 155 if (value) 156 dat |= 1 << offset; 157 else 158 dat &= ~(1 << offset); 159 160 __raw_writel(dat, base + GPIODAT_OFF); 161 __raw_writel(con, regcon); 162 __raw_writel(dat, base + GPIODAT_OFF); 163 164 gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); 165 166 return 0; 167} 168 169void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) 170{ 171 chip->chip.direction_input = samsung_gpiolib_4bit_input; 172 chip->chip.direction_output = samsung_gpiolib_4bit_output; 173 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); 174} 175 176void __init samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) 177{ 178 chip->chip.direction_input = samsung_gpiolib_4bit2_input; 179 chip->chip.direction_output = samsung_gpiolib_4bit2_output; 180 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); 181} 182 183void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, 184 int nr_chips) 185{ 186 for (; nr_chips > 0; nr_chips--, chip++) { 187 samsung_gpiolib_add_4bit(chip); 188 s3c_gpiolib_add(chip); 189 } 190} 191 192void __init samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, 193 int nr_chips) 194{ 195 for (; nr_chips > 0; nr_chips--, chip++) { 196 samsung_gpiolib_add_4bit2(chip); 197 s3c_gpiolib_add(chip); 198 } 199} 200