0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/spinlock.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/gpio/driver.h>
0013 #include <linux/io.h>
0014 #include <linux/cs5535.h>
0015 #include <asm/msr.h>
0016
0017 #define DRV_NAME "cs5535-gpio"
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 #define GPIO_DEFAULT_MASK 0x0F7FFFFF
0037
0038 static ulong mask = GPIO_DEFAULT_MASK;
0039 module_param_named(mask, mask, ulong, 0444);
0040 MODULE_PARM_DESC(mask, "GPIO channel mask.");
0041
0042
0043
0044
0045
0046 static struct cs5535_gpio_chip {
0047 struct gpio_chip chip;
0048 resource_size_t base;
0049
0050 struct platform_device *pdev;
0051 spinlock_t lock;
0052 } cs5535_gpio_chip;
0053
0054
0055
0056
0057
0058
0059
0060 static void errata_outl(struct cs5535_gpio_chip *chip, u32 val,
0061 unsigned int reg)
0062 {
0063 unsigned long addr = chip->base + 0x80 + reg;
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074 if (reg != GPIO_POSITIVE_EDGE_STS && reg != GPIO_NEGATIVE_EDGE_STS) {
0075 if (val & 0xffff)
0076 val |= (inl(addr) & 0xffff);
0077 else
0078 val |= (inl(addr) ^ (val >> 16));
0079 }
0080 outl(val, addr);
0081 }
0082
0083 static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset,
0084 unsigned int reg)
0085 {
0086 if (offset < 16)
0087
0088 outl(1 << offset, chip->base + reg);
0089 else
0090
0091 errata_outl(chip, 1 << (offset - 16), reg);
0092 }
0093
0094 void cs5535_gpio_set(unsigned offset, unsigned int reg)
0095 {
0096 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
0097 unsigned long flags;
0098
0099 spin_lock_irqsave(&chip->lock, flags);
0100 __cs5535_gpio_set(chip, offset, reg);
0101 spin_unlock_irqrestore(&chip->lock, flags);
0102 }
0103 EXPORT_SYMBOL_GPL(cs5535_gpio_set);
0104
0105 static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset,
0106 unsigned int reg)
0107 {
0108 if (offset < 16)
0109
0110 outl(1 << (offset + 16), chip->base + reg);
0111 else
0112
0113 errata_outl(chip, 1 << offset, reg);
0114 }
0115
0116 void cs5535_gpio_clear(unsigned offset, unsigned int reg)
0117 {
0118 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
0119 unsigned long flags;
0120
0121 spin_lock_irqsave(&chip->lock, flags);
0122 __cs5535_gpio_clear(chip, offset, reg);
0123 spin_unlock_irqrestore(&chip->lock, flags);
0124 }
0125 EXPORT_SYMBOL_GPL(cs5535_gpio_clear);
0126
0127 int cs5535_gpio_isset(unsigned offset, unsigned int reg)
0128 {
0129 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
0130 unsigned long flags;
0131 long val;
0132
0133 spin_lock_irqsave(&chip->lock, flags);
0134 if (offset < 16)
0135
0136 val = inl(chip->base + reg);
0137 else {
0138
0139 val = inl(chip->base + 0x80 + reg);
0140 offset -= 16;
0141 }
0142 spin_unlock_irqrestore(&chip->lock, flags);
0143
0144 return (val & (1 << offset)) ? 1 : 0;
0145 }
0146 EXPORT_SYMBOL_GPL(cs5535_gpio_isset);
0147
0148 int cs5535_gpio_set_irq(unsigned group, unsigned irq)
0149 {
0150 uint32_t lo, hi;
0151
0152 if (group > 7 || irq > 15)
0153 return -EINVAL;
0154
0155 rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
0156
0157 lo &= ~(0xF << (group * 4));
0158 lo |= (irq & 0xF) << (group * 4);
0159
0160 wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi);
0161 return 0;
0162 }
0163 EXPORT_SYMBOL_GPL(cs5535_gpio_set_irq);
0164
0165 void cs5535_gpio_setup_event(unsigned offset, int pair, int pme)
0166 {
0167 struct cs5535_gpio_chip *chip = &cs5535_gpio_chip;
0168 uint32_t shift = (offset % 8) * 4;
0169 unsigned long flags;
0170 uint32_t val;
0171
0172 if (offset >= 24)
0173 offset = GPIO_MAP_W;
0174 else if (offset >= 16)
0175 offset = GPIO_MAP_Z;
0176 else if (offset >= 8)
0177 offset = GPIO_MAP_Y;
0178 else
0179 offset = GPIO_MAP_X;
0180
0181 spin_lock_irqsave(&chip->lock, flags);
0182 val = inl(chip->base + offset);
0183
0184
0185 val &= ~(0xF << shift);
0186
0187
0188 val |= ((pair & 7) << shift);
0189
0190
0191 if (pme)
0192 val |= (1 << (shift + 3));
0193
0194 outl(val, chip->base + offset);
0195 spin_unlock_irqrestore(&chip->lock, flags);
0196 }
0197 EXPORT_SYMBOL_GPL(cs5535_gpio_setup_event);
0198
0199
0200
0201
0202
0203 static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
0204 {
0205 struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
0206 unsigned long flags;
0207
0208 spin_lock_irqsave(&chip->lock, flags);
0209
0210
0211 if ((mask & (1 << offset)) == 0) {
0212 dev_info(&chip->pdev->dev,
0213 "pin %u is not available (check mask)\n", offset);
0214 spin_unlock_irqrestore(&chip->lock, flags);
0215 return -EINVAL;
0216 }
0217
0218
0219 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1);
0220 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2);
0221
0222
0223 __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1);
0224
0225 spin_unlock_irqrestore(&chip->lock, flags);
0226
0227 return 0;
0228 }
0229
0230 static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
0231 {
0232 return cs5535_gpio_isset(offset, GPIO_READ_BACK);
0233 }
0234
0235 static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
0236 {
0237 if (val)
0238 cs5535_gpio_set(offset, GPIO_OUTPUT_VAL);
0239 else
0240 cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL);
0241 }
0242
0243 static int chip_direction_input(struct gpio_chip *c, unsigned offset)
0244 {
0245 struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
0246 unsigned long flags;
0247
0248 spin_lock_irqsave(&chip->lock, flags);
0249 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
0250 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
0251 spin_unlock_irqrestore(&chip->lock, flags);
0252
0253 return 0;
0254 }
0255
0256 static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
0257 {
0258 struct cs5535_gpio_chip *chip = gpiochip_get_data(c);
0259 unsigned long flags;
0260
0261 spin_lock_irqsave(&chip->lock, flags);
0262
0263 __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
0264 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
0265 if (val)
0266 __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
0267 else
0268 __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL);
0269
0270 spin_unlock_irqrestore(&chip->lock, flags);
0271
0272 return 0;
0273 }
0274
0275 static const char * const cs5535_gpio_names[] = {
0276 "GPIO0", "GPIO1", "GPIO2", "GPIO3",
0277 "GPIO4", "GPIO5", "GPIO6", "GPIO7",
0278 "GPIO8", "GPIO9", "GPIO10", "GPIO11",
0279 "GPIO12", "GPIO13", "GPIO14", "GPIO15",
0280 "GPIO16", "GPIO17", "GPIO18", "GPIO19",
0281 "GPIO20", "GPIO21", "GPIO22", NULL,
0282 "GPIO24", "GPIO25", "GPIO26", "GPIO27",
0283 "GPIO28", NULL, NULL, NULL,
0284 };
0285
0286 static struct cs5535_gpio_chip cs5535_gpio_chip = {
0287 .chip = {
0288 .owner = THIS_MODULE,
0289 .label = DRV_NAME,
0290
0291 .base = 0,
0292 .ngpio = 32,
0293 .names = cs5535_gpio_names,
0294 .request = chip_gpio_request,
0295
0296 .get = chip_gpio_get,
0297 .set = chip_gpio_set,
0298
0299 .direction_input = chip_direction_input,
0300 .direction_output = chip_direction_output,
0301 },
0302 };
0303
0304 static int cs5535_gpio_probe(struct platform_device *pdev)
0305 {
0306 struct resource *res;
0307 int err = -EIO;
0308 ulong mask_orig = mask;
0309
0310
0311
0312
0313
0314
0315
0316
0317 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
0318 if (!res) {
0319 dev_err(&pdev->dev, "can't fetch device resource info\n");
0320 return err;
0321 }
0322
0323 if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
0324 pdev->name)) {
0325 dev_err(&pdev->dev, "can't request region\n");
0326 return err;
0327 }
0328
0329
0330 cs5535_gpio_chip.base = res->start;
0331 cs5535_gpio_chip.pdev = pdev;
0332 spin_lock_init(&cs5535_gpio_chip.lock);
0333
0334 dev_info(&pdev->dev, "reserved resource region %pR\n", res);
0335
0336
0337 mask &= 0x1F7FFFFF;
0338
0339
0340
0341 mask &= ~(1 << 28);
0342
0343 if (mask_orig != mask)
0344 dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n",
0345 mask_orig, mask);
0346
0347
0348 return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
0349 &cs5535_gpio_chip);
0350 }
0351
0352 static struct platform_driver cs5535_gpio_driver = {
0353 .driver = {
0354 .name = DRV_NAME,
0355 },
0356 .probe = cs5535_gpio_probe,
0357 };
0358
0359 module_platform_driver(cs5535_gpio_driver);
0360
0361 MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
0362 MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver");
0363 MODULE_LICENSE("GPL");
0364 MODULE_ALIAS("platform:" DRV_NAME);