0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/device.h>
0012 #include <linux/init.h>
0013 #include <linux/io.h>
0014 #include <linux/gpio.h>
0015
0016 #include "gpio-samsung.h"
0017
0018 #include "gpio-core.h"
0019 #include "pm.h"
0020
0021
0022
0023 #define OFFS_CON (0x00)
0024 #define OFFS_DAT (0x04)
0025 #define OFFS_UP (0x08)
0026
0027 static void samsung_gpio_pm_1bit_save(struct samsung_gpio_chip *chip)
0028 {
0029 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
0030 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
0031 }
0032
0033 static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip)
0034 {
0035 void __iomem *base = chip->base;
0036 u32 old_gpcon = __raw_readl(base + OFFS_CON);
0037 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
0038 u32 gps_gpcon = chip->pm_save[0];
0039 u32 gps_gpdat = chip->pm_save[1];
0040 u32 gpcon;
0041
0042
0043
0044
0045
0046
0047 gpcon = old_gpcon | gps_gpcon;
0048 __raw_writel(gpcon, base + OFFS_CON);
0049
0050
0051
0052 __raw_writel(gps_gpdat, base + OFFS_DAT);
0053 __raw_writel(gps_gpcon, base + OFFS_CON);
0054
0055 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
0056 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
0057 }
0058
0059 struct samsung_gpio_pm samsung_gpio_pm_1bit = {
0060 .save = samsung_gpio_pm_1bit_save,
0061 .resume = samsung_gpio_pm_1bit_resume,
0062 };
0063
0064 static void samsung_gpio_pm_2bit_save(struct samsung_gpio_chip *chip)
0065 {
0066 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
0067 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
0068 chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP);
0069 }
0070
0071
0072
0073
0074 static inline int is_sfn(unsigned long con)
0075 {
0076 return con >= 2;
0077 }
0078
0079
0080
0081 static inline int is_in(unsigned long con)
0082 {
0083 return con == 0;
0084 }
0085
0086
0087
0088 static inline int is_out(unsigned long con)
0089 {
0090 return con == 1;
0091 }
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120 static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip)
0121 {
0122 void __iomem *base = chip->base;
0123 u32 old_gpcon = __raw_readl(base + OFFS_CON);
0124 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
0125 u32 gps_gpcon = chip->pm_save[0];
0126 u32 gps_gpdat = chip->pm_save[1];
0127 u32 gpcon, old, new, mask;
0128 u32 change_mask = 0x0;
0129 int nr;
0130
0131
0132 __raw_writel(chip->pm_save[2], base + OFFS_UP);
0133
0134
0135
0136
0137
0138
0139 for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
0140 old = (old_gpcon & mask) >> nr;
0141 new = (gps_gpcon & mask) >> nr;
0142
0143
0144
0145 if (old == new)
0146 continue;
0147
0148
0149
0150 if (is_sfn(old) && is_sfn(new))
0151 continue;
0152
0153
0154
0155 if (is_in(old) && is_out(new))
0156 continue;
0157
0158
0159
0160 if (is_sfn(old) && is_out(new))
0161 continue;
0162
0163
0164
0165
0166 change_mask |= mask;
0167 }
0168
0169
0170
0171
0172 gpcon = old_gpcon & ~change_mask;
0173 gpcon |= gps_gpcon & change_mask;
0174
0175 __raw_writel(gpcon, base + OFFS_CON);
0176
0177
0178
0179 __raw_writel(gps_gpdat, base + OFFS_DAT);
0180 __raw_writel(gps_gpcon, base + OFFS_CON);
0181
0182 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
0183 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
0184 }
0185
0186 struct samsung_gpio_pm samsung_gpio_pm_2bit = {
0187 .save = samsung_gpio_pm_2bit_save,
0188 .resume = samsung_gpio_pm_2bit_resume,
0189 };
0190
0191 #if defined(CONFIG_ARCH_S3C64XX)
0192 static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip)
0193 {
0194 chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
0195 chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT);
0196 chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP);
0197
0198 if (chip->chip.ngpio > 8)
0199 chip->pm_save[0] = __raw_readl(chip->base - 4);
0200 }
0201
0202 static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
0203 {
0204 u32 old, new, mask;
0205 u32 change_mask = 0x0;
0206 int nr;
0207
0208 for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) {
0209 old = (old_gpcon & mask) >> nr;
0210 new = (gps_gpcon & mask) >> nr;
0211
0212
0213
0214 if (old == new)
0215 continue;
0216
0217
0218
0219 if (is_sfn(old) && is_sfn(new))
0220 continue;
0221
0222
0223
0224 if (is_in(old) && is_out(new))
0225 continue;
0226
0227
0228
0229 if (is_sfn(old) && is_out(new))
0230 continue;
0231
0232
0233
0234
0235 change_mask |= mask;
0236 }
0237
0238 return change_mask;
0239 }
0240
0241 static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index)
0242 {
0243 void __iomem *con = chip->base + (index * 4);
0244 u32 old_gpcon = __raw_readl(con);
0245 u32 gps_gpcon = chip->pm_save[index + 1];
0246 u32 gpcon, mask;
0247
0248 mask = samsung_gpio_pm_4bit_mask(old_gpcon, gps_gpcon);
0249
0250 gpcon = old_gpcon & ~mask;
0251 gpcon |= gps_gpcon & mask;
0252
0253 __raw_writel(gpcon, con);
0254 }
0255
0256 static void samsung_gpio_pm_4bit_resume(struct samsung_gpio_chip *chip)
0257 {
0258 void __iomem *base = chip->base;
0259 u32 old_gpcon[2];
0260 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
0261 u32 gps_gpdat = chip->pm_save[2];
0262
0263
0264
0265 old_gpcon[0] = 0;
0266 old_gpcon[1] = __raw_readl(base + OFFS_CON);
0267
0268 samsung_gpio_pm_4bit_con(chip, 0);
0269 if (chip->chip.ngpio > 8) {
0270 old_gpcon[0] = __raw_readl(base - 4);
0271 samsung_gpio_pm_4bit_con(chip, -1);
0272 }
0273
0274
0275
0276 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
0277 __raw_writel(chip->pm_save[1], base + OFFS_CON);
0278 if (chip->chip.ngpio > 8)
0279 __raw_writel(chip->pm_save[0], base - 4);
0280
0281 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
0282 __raw_writel(chip->pm_save[3], base + OFFS_UP);
0283
0284 if (chip->chip.ngpio > 8) {
0285 S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n",
0286 chip->chip.label, old_gpcon[0], old_gpcon[1],
0287 __raw_readl(base - 4),
0288 __raw_readl(base + OFFS_CON),
0289 old_gpdat, gps_gpdat);
0290 } else
0291 S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n",
0292 chip->chip.label, old_gpcon[1],
0293 __raw_readl(base + OFFS_CON),
0294 old_gpdat, gps_gpdat);
0295 }
0296
0297 struct samsung_gpio_pm samsung_gpio_pm_4bit = {
0298 .save = samsung_gpio_pm_4bit_save,
0299 .resume = samsung_gpio_pm_4bit_resume,
0300 };
0301 #endif
0302
0303
0304
0305
0306
0307 static void samsung_pm_save_gpio(struct samsung_gpio_chip *ourchip)
0308 {
0309 struct samsung_gpio_pm *pm = ourchip->pm;
0310
0311 if (pm == NULL || pm->save == NULL)
0312 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
0313 else
0314 pm->save(ourchip);
0315 }
0316
0317
0318
0319
0320
0321
0322
0323 void samsung_pm_save_gpios(void)
0324 {
0325 struct samsung_gpio_chip *ourchip;
0326 unsigned int gpio_nr;
0327
0328 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) {
0329 ourchip = samsung_gpiolib_getchip(gpio_nr);
0330 if (!ourchip) {
0331 gpio_nr++;
0332 continue;
0333 }
0334
0335 samsung_pm_save_gpio(ourchip);
0336
0337 S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n",
0338 ourchip->chip.label,
0339 ourchip->pm_save[0],
0340 ourchip->pm_save[1],
0341 ourchip->pm_save[2],
0342 ourchip->pm_save[3]);
0343
0344 gpio_nr += ourchip->chip.ngpio;
0345 gpio_nr += CONFIG_S3C_GPIO_SPACE;
0346 }
0347 }
0348
0349
0350
0351
0352
0353 static void samsung_pm_resume_gpio(struct samsung_gpio_chip *ourchip)
0354 {
0355 struct samsung_gpio_pm *pm = ourchip->pm;
0356
0357 if (pm == NULL || pm->resume == NULL)
0358 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
0359 else
0360 pm->resume(ourchip);
0361 }
0362
0363 void samsung_pm_restore_gpios(void)
0364 {
0365 struct samsung_gpio_chip *ourchip;
0366 unsigned int gpio_nr;
0367
0368 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) {
0369 ourchip = samsung_gpiolib_getchip(gpio_nr);
0370 if (!ourchip) {
0371 gpio_nr++;
0372 continue;
0373 }
0374
0375 samsung_pm_resume_gpio(ourchip);
0376
0377 gpio_nr += ourchip->chip.ngpio;
0378 gpio_nr += CONFIG_S3C_GPIO_SPACE;
0379 }
0380 }