diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-07-25 04:45:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:29 -0400 |
commit | f61be273d3699d174bc1438e6804f9f9e52bb932 (patch) | |
tree | 3d9940d725e2837168b8b072140b85b00c198896 /drivers | |
parent | 472dba7d117844c746be97db6be26c2810d79b62 (diff) |
sm501: add gpiolib support
Add support for exporting the GPIOs on the SM501 via gpiolib.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Cc: Arnaud Patard <apatard@mandriva.com>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mfd/sm501.c | 299 |
2 files changed, 255 insertions, 52 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9f93c29fed35..bac9e973ece0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -19,6 +19,14 @@ config MFD_SM501 | |||
19 | interface. The device may be connected by PCI or local bus with | 19 | interface. The device may be connected by PCI or local bus with |
20 | varying functions enabled. | 20 | varying functions enabled. |
21 | 21 | ||
22 | config MFD_SM501_GPIO | ||
23 | bool "Export GPIO via GPIO layer" | ||
24 | depends on MFD_SM501 && HAVE_GPIO_LIB | ||
25 | ---help--- | ||
26 | This option uses the gpio library layer to export the 64 GPIO | ||
27 | lines on the SM501. The platform data is used to supply the | ||
28 | base number for the first GPIO line to register. | ||
29 | |||
22 | config MFD_ASIC3 | 30 | config MFD_ASIC3 |
23 | bool "Support for Compaq ASIC3" | 31 | bool "Support for Compaq ASIC3" |
24 | depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM | 32 | depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM |
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 9296b2673b52..be8713908125 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
22 | #include <linux/gpio.h> | ||
22 | 23 | ||
23 | #include <linux/sm501.h> | 24 | #include <linux/sm501.h> |
24 | #include <linux/sm501-regs.h> | 25 | #include <linux/sm501-regs.h> |
@@ -31,10 +32,29 @@ struct sm501_device { | |||
31 | struct platform_device pdev; | 32 | struct platform_device pdev; |
32 | }; | 33 | }; |
33 | 34 | ||
35 | struct sm501_gpio; | ||
36 | |||
37 | struct sm501_gpio_chip { | ||
38 | struct gpio_chip gpio; | ||
39 | struct sm501_gpio *ourgpio; /* to get back to parent. */ | ||
40 | void __iomem *regbase; | ||
41 | }; | ||
42 | |||
43 | struct sm501_gpio { | ||
44 | struct sm501_gpio_chip low; | ||
45 | struct sm501_gpio_chip high; | ||
46 | spinlock_t lock; | ||
47 | |||
48 | unsigned int registered : 1; | ||
49 | void __iomem *regs; | ||
50 | struct resource *regs_res; | ||
51 | }; | ||
52 | |||
34 | struct sm501_devdata { | 53 | struct sm501_devdata { |
35 | spinlock_t reg_lock; | 54 | spinlock_t reg_lock; |
36 | struct mutex clock_lock; | 55 | struct mutex clock_lock; |
37 | struct list_head devices; | 56 | struct list_head devices; |
57 | struct sm501_gpio gpio; | ||
38 | 58 | ||
39 | struct device *dev; | 59 | struct device *dev; |
40 | struct resource *io_res; | 60 | struct resource *io_res; |
@@ -42,6 +62,7 @@ struct sm501_devdata { | |||
42 | struct resource *regs_claim; | 62 | struct resource *regs_claim; |
43 | struct sm501_platdata *platdata; | 63 | struct sm501_platdata *platdata; |
44 | 64 | ||
65 | |||
45 | unsigned int in_suspend; | 66 | unsigned int in_suspend; |
46 | unsigned long pm_misc; | 67 | unsigned long pm_misc; |
47 | 68 | ||
@@ -52,6 +73,7 @@ struct sm501_devdata { | |||
52 | unsigned int rev; | 73 | unsigned int rev; |
53 | }; | 74 | }; |
54 | 75 | ||
76 | |||
55 | #define MHZ (1000 * 1000) | 77 | #define MHZ (1000 * 1000) |
56 | 78 | ||
57 | #ifdef DEBUG | 79 | #ifdef DEBUG |
@@ -276,58 +298,6 @@ unsigned long sm501_modify_reg(struct device *dev, | |||
276 | 298 | ||
277 | EXPORT_SYMBOL_GPL(sm501_modify_reg); | 299 | EXPORT_SYMBOL_GPL(sm501_modify_reg); |
278 | 300 | ||
279 | unsigned long sm501_gpio_get(struct device *dev, | ||
280 | unsigned long gpio) | ||
281 | { | ||
282 | struct sm501_devdata *sm = dev_get_drvdata(dev); | ||
283 | unsigned long result; | ||
284 | unsigned long reg; | ||
285 | |||
286 | reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; | ||
287 | result = readl(sm->regs + reg); | ||
288 | |||
289 | result >>= (gpio & 31); | ||
290 | return result & 1UL; | ||
291 | } | ||
292 | |||
293 | EXPORT_SYMBOL_GPL(sm501_gpio_get); | ||
294 | |||
295 | void sm501_gpio_set(struct device *dev, | ||
296 | unsigned long gpio, | ||
297 | unsigned int to, | ||
298 | unsigned int dir) | ||
299 | { | ||
300 | struct sm501_devdata *sm = dev_get_drvdata(dev); | ||
301 | |||
302 | unsigned long bit = 1 << (gpio & 31); | ||
303 | unsigned long base; | ||
304 | unsigned long save; | ||
305 | unsigned long val; | ||
306 | |||
307 | base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; | ||
308 | base += SM501_GPIO; | ||
309 | |||
310 | spin_lock_irqsave(&sm->reg_lock, save); | ||
311 | |||
312 | val = readl(sm->regs + base) & ~bit; | ||
313 | if (to) | ||
314 | val |= bit; | ||
315 | writel(val, sm->regs + base); | ||
316 | |||
317 | val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit; | ||
318 | if (dir) | ||
319 | val |= bit; | ||
320 | |||
321 | writel(val, sm->regs + SM501_GPIO_DDR_LOW); | ||
322 | sm501_sync_regs(sm); | ||
323 | |||
324 | spin_unlock_irqrestore(&sm->reg_lock, save); | ||
325 | |||
326 | } | ||
327 | |||
328 | EXPORT_SYMBOL_GPL(sm501_gpio_set); | ||
329 | |||
330 | |||
331 | /* sm501_unit_power | 301 | /* sm501_unit_power |
332 | * | 302 | * |
333 | * alters the power active gate to set specific units on or off | 303 | * alters the power active gate to set specific units on or off |
@@ -906,6 +876,226 @@ static int sm501_register_display(struct sm501_devdata *sm, | |||
906 | return sm501_register_device(sm, pdev); | 876 | return sm501_register_device(sm, pdev); |
907 | } | 877 | } |
908 | 878 | ||
879 | #ifdef CONFIG_MFD_SM501_GPIO | ||
880 | |||
881 | static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc) | ||
882 | { | ||
883 | return container_of(gc, struct sm501_gpio_chip, gpio); | ||
884 | } | ||
885 | |||
886 | static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) | ||
887 | { | ||
888 | return container_of(gpio, struct sm501_devdata, gpio); | ||
889 | } | ||
890 | |||
891 | static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
892 | |||
893 | { | ||
894 | struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip); | ||
895 | unsigned long result; | ||
896 | |||
897 | result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW); | ||
898 | result >>= offset; | ||
899 | |||
900 | return result & 1UL; | ||
901 | } | ||
902 | |||
903 | static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
904 | |||
905 | { | ||
906 | struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); | ||
907 | struct sm501_gpio *smgpio = smchip->ourgpio; | ||
908 | unsigned long bit = 1 << offset; | ||
909 | void __iomem *regs = smchip->regbase; | ||
910 | unsigned long save; | ||
911 | unsigned long val; | ||
912 | |||
913 | dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", | ||
914 | __func__, chip, offset); | ||
915 | |||
916 | spin_lock_irqsave(&smgpio->lock, save); | ||
917 | |||
918 | val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit; | ||
919 | if (value) | ||
920 | val |= bit; | ||
921 | writel(val, regs); | ||
922 | |||
923 | sm501_sync_regs(sm501_gpio_to_dev(smgpio)); | ||
924 | spin_unlock_irqrestore(&smgpio->lock, save); | ||
925 | } | ||
926 | |||
927 | static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) | ||
928 | { | ||
929 | struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); | ||
930 | struct sm501_gpio *smgpio = smchip->ourgpio; | ||
931 | void __iomem *regs = smchip->regbase; | ||
932 | unsigned long bit = 1 << offset; | ||
933 | unsigned long save; | ||
934 | unsigned long ddr; | ||
935 | |||
936 | dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", | ||
937 | __func__, chip, offset); | ||
938 | |||
939 | spin_lock_irqsave(&smgpio->lock, save); | ||
940 | |||
941 | ddr = readl(regs + SM501_GPIO_DDR_LOW); | ||
942 | writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); | ||
943 | |||
944 | sm501_sync_regs(sm501_gpio_to_dev(smgpio)); | ||
945 | spin_unlock_irqrestore(&smgpio->lock, save); | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | static int sm501_gpio_output(struct gpio_chip *chip, | ||
951 | unsigned offset, int value) | ||
952 | { | ||
953 | struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); | ||
954 | struct sm501_gpio *smgpio = smchip->ourgpio; | ||
955 | unsigned long bit = 1 << offset; | ||
956 | void __iomem *regs = smchip->regbase; | ||
957 | unsigned long save; | ||
958 | unsigned long val; | ||
959 | unsigned long ddr; | ||
960 | |||
961 | dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", | ||
962 | __func__, chip, offset, value); | ||
963 | |||
964 | spin_lock_irqsave(&smgpio->lock, save); | ||
965 | |||
966 | val = readl(regs + SM501_GPIO_DATA_LOW); | ||
967 | if (value) | ||
968 | val |= bit; | ||
969 | else | ||
970 | val &= ~bit; | ||
971 | writel(val, regs); | ||
972 | |||
973 | ddr = readl(regs + SM501_GPIO_DDR_LOW); | ||
974 | writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); | ||
975 | |||
976 | sm501_sync_regs(sm501_gpio_to_dev(smgpio)); | ||
977 | writel(val, regs + SM501_GPIO_DATA_LOW); | ||
978 | |||
979 | sm501_sync_regs(sm501_gpio_to_dev(smgpio)); | ||
980 | spin_unlock_irqrestore(&smgpio->lock, save); | ||
981 | |||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | static struct gpio_chip gpio_chip_template = { | ||
986 | .ngpio = 32, | ||
987 | .direction_input = sm501_gpio_input, | ||
988 | .direction_output = sm501_gpio_output, | ||
989 | .set = sm501_gpio_set, | ||
990 | .get = sm501_gpio_get, | ||
991 | }; | ||
992 | |||
993 | static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, | ||
994 | struct sm501_gpio *gpio, | ||
995 | struct sm501_gpio_chip *chip) | ||
996 | { | ||
997 | struct sm501_platdata *pdata = sm->platdata; | ||
998 | struct gpio_chip *gchip = &chip->gpio; | ||
999 | unsigned base = pdata->gpio_base; | ||
1000 | |||
1001 | memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip)); | ||
1002 | |||
1003 | if (chip == &gpio->high) { | ||
1004 | base += 32; | ||
1005 | chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; | ||
1006 | gchip->label = "SM501-HIGH"; | ||
1007 | } else { | ||
1008 | chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; | ||
1009 | gchip->label = "SM501-LOW"; | ||
1010 | } | ||
1011 | |||
1012 | gchip->base = base; | ||
1013 | chip->ourgpio = gpio; | ||
1014 | |||
1015 | return gpiochip_add(gchip); | ||
1016 | } | ||
1017 | |||
1018 | static int sm501_register_gpio(struct sm501_devdata *sm) | ||
1019 | { | ||
1020 | struct sm501_gpio *gpio = &sm->gpio; | ||
1021 | resource_size_t iobase = sm->io_res->start + SM501_GPIO; | ||
1022 | int ret; | ||
1023 | int tmp; | ||
1024 | |||
1025 | dev_dbg(sm->dev, "registering gpio block %08llx\n", | ||
1026 | (unsigned long long)iobase); | ||
1027 | |||
1028 | spin_lock_init(&gpio->lock); | ||
1029 | |||
1030 | gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); | ||
1031 | if (gpio->regs_res == NULL) { | ||
1032 | dev_err(sm->dev, "gpio: failed to request region\n"); | ||
1033 | return -ENXIO; | ||
1034 | } | ||
1035 | |||
1036 | gpio->regs = ioremap(iobase, 0x20); | ||
1037 | if (gpio->regs == NULL) { | ||
1038 | dev_err(sm->dev, "gpio: failed to remap registers\n"); | ||
1039 | ret = -ENXIO; | ||
1040 | goto err_mapped; | ||
1041 | } | ||
1042 | |||
1043 | /* Register both our chips. */ | ||
1044 | |||
1045 | ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); | ||
1046 | if (ret) { | ||
1047 | dev_err(sm->dev, "failed to add low chip\n"); | ||
1048 | goto err_mapped; | ||
1049 | } | ||
1050 | |||
1051 | ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); | ||
1052 | if (ret) { | ||
1053 | dev_err(sm->dev, "failed to add high chip\n"); | ||
1054 | goto err_low_chip; | ||
1055 | } | ||
1056 | |||
1057 | gpio->registered = 1; | ||
1058 | |||
1059 | return 0; | ||
1060 | |||
1061 | err_low_chip: | ||
1062 | tmp = gpiochip_remove(&gpio->low.gpio); | ||
1063 | if (tmp) { | ||
1064 | dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); | ||
1065 | return ret; | ||
1066 | } | ||
1067 | |||
1068 | err_mapped: | ||
1069 | release_resource(gpio->regs_res); | ||
1070 | kfree(gpio->regs_res); | ||
1071 | |||
1072 | return ret; | ||
1073 | } | ||
1074 | |||
1075 | static void sm501_gpio_remove(struct sm501_devdata *sm) | ||
1076 | { | ||
1077 | int ret; | ||
1078 | |||
1079 | ret = gpiochip_remove(&sm->gpio.low.gpio); | ||
1080 | if (ret) | ||
1081 | dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); | ||
1082 | |||
1083 | ret = gpiochip_remove(&sm->gpio.high.gpio); | ||
1084 | if (ret) | ||
1085 | dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); | ||
1086 | } | ||
1087 | |||
1088 | #else | ||
1089 | static int sm501_register_gpio(struct sm501_devdata *sm) | ||
1090 | { | ||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | static void sm501_gpio_remove(struct sm501_devdata *sm) | ||
1095 | { | ||
1096 | } | ||
1097 | #endif | ||
1098 | |||
909 | /* sm501_dbg_regs | 1099 | /* sm501_dbg_regs |
910 | * | 1100 | * |
911 | * Debug attribute to attach to parent device to show core registers | 1101 | * Debug attribute to attach to parent device to show core registers |
@@ -1059,6 +1249,8 @@ static int sm501_init_dev(struct sm501_devdata *sm) | |||
1059 | sm501_register_usbhost(sm, &mem_avail); | 1249 | sm501_register_usbhost(sm, &mem_avail); |
1060 | if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) | 1250 | if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) |
1061 | sm501_register_uart(sm, idata->devices); | 1251 | sm501_register_uart(sm, idata->devices); |
1252 | if (idata->devices & SM501_USE_GPIO) | ||
1253 | sm501_register_gpio(sm); | ||
1062 | } | 1254 | } |
1063 | 1255 | ||
1064 | ret = sm501_check_clocks(sm); | 1256 | ret = sm501_check_clocks(sm); |
@@ -1366,6 +1558,9 @@ static void sm501_dev_remove(struct sm501_devdata *sm) | |||
1366 | sm501_remove_sub(sm, smdev); | 1558 | sm501_remove_sub(sm, smdev); |
1367 | 1559 | ||
1368 | device_remove_file(sm->dev, &dev_attr_dbg_regs); | 1560 | device_remove_file(sm->dev, &dev_attr_dbg_regs); |
1561 | |||
1562 | if (sm->gpio.registered) | ||
1563 | sm501_gpio_remove(sm); | ||
1369 | } | 1564 | } |
1370 | 1565 | ||
1371 | static void sm501_pci_remove(struct pci_dev *dev) | 1566 | static void sm501_pci_remove(struct pci_dev *dev) |