Advertisement
Guest User

Untitled

a guest
Jun 14th, 2015
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.32 KB | None | 0 0
  1. commit 90bf20c80db47eca56d6a2099121218c2d5e47c9
  2. Author: Tobias Diedrich <ranma+coreboot@tdiedrich.de>
  3. Date: Sat Jun 13 21:36:39 2015 +0200
  4.  
  5. Tmp: Add gpio-sb8xx driver
  6.  
  7. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
  8. index caefe80..f4d7dd5 100644
  9. --- a/drivers/gpio/Kconfig
  10. +++ b/drivers/gpio/Kconfig
  11. @@ -884,6 +884,18 @@ config GPIO_BT8XX
  12.  
  13. If unsure, say N.
  14.  
  15. +config GPIO_SB8XX
  16. + tristate "AMD SB8XX GPIO driver"
  17. + depends on PCI
  18. + depends on GPIO_ACPI
  19. + help
  20. + Say Y here to support AMD SB8XX/SB9XX/A5X/A8X south bridge GPIO.
  21. +
  22. + This driver also requires a corresponding entry in the ACPI DSDT,
  23. + see the documentation file at Documentation/gpio/gpio-sb8xx.txt
  24. +
  25. + If unsure, say N.
  26. +
  27. config GPIO_INTEL_MID
  28. bool "Intel Mid GPIO support"
  29. depends on PCI && X86
  30. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
  31. index f71bb97..b6d7d9d 100644
  32. --- a/drivers/gpio/Makefile
  33. +++ b/drivers/gpio/Makefile
  34. @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
  35. obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
  36. obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
  37. obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
  38. +obj-$(CONFIG_GPIO_SB8XX) += gpio-sb8xx.o
  39. obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
  40. obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
  41. obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
  42. diff --git a/drivers/gpio/gpio-sb8xx.c b/drivers/gpio/gpio-sb8xx.c
  43. new file mode 100644
  44. index 0000000..ead6f78
  45. --- /dev/null
  46. +++ b/drivers/gpio/gpio-sb8xx.c
  47. @@ -0,0 +1,313 @@
  48. +/*
  49. + * GPIO driver for AMD SB8XX/SB9XX/A5X/A8X south bridges
  50. + *
  51. + * Copyright (c) 2015 Tobias Diedrich
  52. + *
  53. + * Based on the AMD 8111 GPIO driver:
  54. + * Copyright (c) 2012 Dmitry Eremin-Solenikov
  55. + *
  56. + * Based on the AMD RNG driver:
  57. + * Copyright 2005 (c) MontaVista Software, Inc.
  58. + * with the majority of the code coming from:
  59. + *
  60. + * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
  61. + * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
  62. + *
  63. + * derived from
  64. + *
  65. + * Hardware driver for the AMD 768 Random Number Generator (RNG)
  66. + * (c) Copyright 2001 Red Hat Inc
  67. + *
  68. + * derived from
  69. + *
  70. + * Hardware driver for Intel i810 Random Number Generator (RNG)
  71. + * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
  72. + * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
  73. + *
  74. + * This file is licensed under the terms of the GNU General Public
  75. + * License version 2. This program is licensed "as is" without any
  76. + * warranty of any kind, whether express or implied.
  77. + */
  78. +#include <linux/module.h>
  79. +#include <linux/kernel.h>
  80. +#include <linux/io.h>
  81. +#include <linux/gpio.h>
  82. +#include <linux/pci.h>
  83. +#include <linux/spinlock.h>
  84. +#include <linux/platform_device.h>
  85. +
  86. +#define PMBASE_OFFSET 0x100
  87. +#define PMBASE_SIZE 0x100
  88. +
  89. +#define MAX_GPIO 256
  90. +
  91. +#define ACPI_MMIO_BAR 0x24
  92. +
  93. +#define REG_GPIO(i) (0x00 + (i))
  94. +
  95. +#define GPIO_OWN_IMC 0x01 /* OwnedByImc */
  96. +#define GPIO_OWN_HOST 0x02 /* OwnedByHost */
  97. +#define GPIO_STICKY 0x04 /* Settings sticky across reset */
  98. +#define GPIO_PULLUP_B 0x08 /* 0 to enable pull-up */
  99. +#define GPIO_PULLDOWN 0x10 /* 1 to enable pull-down */
  100. +#define GPIO_OUT_EN_B 0x20 /* 0 to enable output */
  101. +#define GPIO_OUT 0x40
  102. +#define GPIO_IN 0x80
  103. +
  104. +/*
  105. + * Data for PCI driver interface
  106. + *
  107. + * This data only exists for exporting the supported
  108. + * PCI ids via MODULE_DEVICE_TABLE. We do not actually
  109. + * register a pci_driver, because the smbus driver is
  110. + * using the same PCI id.
  111. + */
  112. +static const struct pci_device_id pci_tbl[] = {
  113. + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS), 0 },
  114. + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS), 0 },
  115. + { 0, }, /* terminate list */
  116. +};
  117. +MODULE_DEVICE_TABLE(pci, pci_tbl);
  118. +
  119. +struct amd_sb8xx_gpio {
  120. + struct gpio_chip chip;
  121. + u32 pmbase;
  122. + void __iomem *pm;
  123. + struct device *dev;
  124. + spinlock_t lock; /* guards hw registers and orig table */
  125. + u8 orig[MAX_GPIO];
  126. +};
  127. +
  128. +#define to_agp(chip) container_of(chip, struct amd_sb8xx_gpio, chip)
  129. +
  130. +static int amd_sb8xx_gpio_request(struct gpio_chip *chip, unsigned offset)
  131. +{
  132. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  133. + u8 orig = ioread8(agp->pm + offset);
  134. +
  135. + if (orig & GPIO_OWN_IMC) {
  136. + dev_err(agp->dev, "Requested gpio %d is owned by IMC\n", offset);
  137. + return -EINVAL;
  138. + }
  139. +
  140. + agp->orig[offset] = orig;
  141. + dev_dbg(agp->dev, "Requested gpio %d, data %x\n", offset, orig);
  142. + return 0;
  143. +}
  144. +
  145. +static void amd_sb8xx_gpio_free(struct gpio_chip *chip, unsigned offset)
  146. +{
  147. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  148. +
  149. + dev_dbg(agp->dev, "Freed gpio %d, data %x\n", offset, agp->orig[offset]);
  150. +
  151. + iowrite8(agp->orig[offset], agp->pm + offset);
  152. +}
  153. +
  154. +static void amd_sb8xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  155. +{
  156. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  157. + u8 temp;
  158. + unsigned long flags;
  159. +
  160. + spin_lock_irqsave(&agp->lock, flags);
  161. + temp = ioread8(agp->pm + offset);
  162. + temp = (temp & ~(GPIO_OUT | GPIO_OUT_EN_B)) | (value ? GPIO_OUT : 0);
  163. + iowrite8(temp, agp->pm + offset);
  164. + spin_unlock_irqrestore(&agp->lock, flags);
  165. +
  166. + dev_dbg(agp->dev, "Setting gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
  167. +}
  168. +
  169. +static int amd_sb8xx_gpio_get(struct gpio_chip *chip, unsigned offset)
  170. +{
  171. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  172. + u8 temp;
  173. +
  174. + temp = ioread8(agp->pm + offset);
  175. +
  176. + dev_dbg(agp->dev, "Getting gpio %d, reg=%02x\n", offset, temp);
  177. +
  178. + return (temp & GPIO_IN) ? 1 : 0;
  179. +}
  180. +
  181. +static int amd_sb8xx_gpio_dirout(struct gpio_chip *chip, unsigned offset, int value)
  182. +{
  183. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  184. + u8 temp;
  185. + unsigned long flags;
  186. +
  187. + spin_lock_irqsave(&agp->lock, flags);
  188. + temp = ioread8(agp->pm + offset);
  189. + temp = (temp & ~(GPIO_OUT | GPIO_OUT_EN_B)) | (value ? GPIO_OUT : 0);
  190. + iowrite8(temp, agp->pm + offset);
  191. + spin_unlock_irqrestore(&agp->lock, flags);
  192. +
  193. + dev_dbg(agp->dev, "Dirout gpio %d, value %d, reg=%02x\n", offset, !!value, temp);
  194. +
  195. + return 0;
  196. +}
  197. +
  198. +static int amd_sb8xx_gpio_dirin(struct gpio_chip *chip, unsigned offset)
  199. +{
  200. + struct amd_sb8xx_gpio *agp = to_agp(chip);
  201. + u8 temp;
  202. + unsigned long flags;
  203. +
  204. + spin_lock_irqsave(&agp->lock, flags);
  205. + temp = ioread8(agp->pm + offset);
  206. + temp = temp | GPIO_OUT_EN_B;
  207. + iowrite8(temp, agp->pm + offset);
  208. + spin_unlock_irqrestore(&agp->lock, flags);
  209. +
  210. + dev_dbg(agp->dev, "Dirin gpio %d, reg=%02x\n", offset, temp);
  211. +
  212. + return 0;
  213. +}
  214. +
  215. +static struct amd_sb8xx_gpio gp = {
  216. + .chip = {
  217. + .label = "AMD SB8XX/SB9XX/A5X/A8X GPIO driver",
  218. + .owner = THIS_MODULE,
  219. + .base = -1,
  220. + .ngpio = 224,
  221. + .request = amd_sb8xx_gpio_request,
  222. + .free = amd_sb8xx_gpio_free,
  223. + .set = amd_sb8xx_gpio_set,
  224. + .get = amd_sb8xx_gpio_get,
  225. + .direction_output = amd_sb8xx_gpio_dirout,
  226. + .direction_input = amd_sb8xx_gpio_dirin,
  227. + },
  228. +};
  229. +
  230. +static u32 amd_acpi_mmio_base(void)
  231. +{
  232. + u32 bar = 0;
  233. + outb_p(ACPI_MMIO_BAR, 0xcd6);
  234. + bar = inb_p(0xcd7);
  235. + outb_p(ACPI_MMIO_BAR + 1, 0xcd6);
  236. + bar |= inb_p(0xcd7) << 8;
  237. + outb_p(ACPI_MMIO_BAR + 2, 0xcd6);
  238. + bar |= inb_p(0xcd7) << 16;
  239. + outb_p(ACPI_MMIO_BAR + 3, 0xcd6);
  240. + bar |= inb_p(0xcd7) << 24;
  241. +
  242. + return bar;
  243. +}
  244. +
  245. +static struct pci_dev *amd_get_sb_pdev(void)
  246. +{
  247. + struct pci_dev *pdev = NULL;
  248. + const struct pci_device_id *ent;
  249. +
  250. + /* We look for our device - AMD South Bridge
  251. + * I don't know about a system with two such bridges,
  252. + * so we can assume that there is max. one device.
  253. + *
  254. + * We can't use plain pci_driver mechanism,
  255. + * as the device is really a multiple function device,
  256. + * main driver that binds to the pci_device is an smbus
  257. + * driver and have to find & bind to the device this way.
  258. + */
  259. + for_each_pci_dev(pdev) {
  260. + ent = pci_match_id(pci_tbl, pdev);
  261. + if (ent)
  262. + return pdev;
  263. + }
  264. +
  265. + return NULL;
  266. +}
  267. +
  268. +static int sb8xx_plat_probe(struct platform_device *pdev)
  269. +{
  270. + int err = -ENODEV;
  271. + struct pci_dev *pcidev = amd_get_sb_pdev();
  272. + u32 acpi_bar = 0;
  273. +
  274. + printk(KERN_INFO "sb8xx_plat_probe()\n");
  275. +
  276. + /* Device not found. */
  277. + if (!pcidev)
  278. + goto out;
  279. +
  280. + acpi_bar = amd_acpi_mmio_base();
  281. + printk(KERN_INFO "Found AMD SB8XX-style southbridge, AcpiMmioEn=0x%08x\n", acpi_bar);
  282. + if ((acpi_bar & 3) != 1) {
  283. + printk(KERN_ERR "acpi bar not memory mapped\n");
  284. + goto out;
  285. + }
  286. +
  287. + gp.pmbase = acpi_bar & ~3;
  288. + printk(KERN_INFO "Requesting region @0x%08x\n", gp.pmbase + PMBASE_OFFSET);
  289. + if (!devm_request_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE, "AMD SB8XX GPIO")) {
  290. + dev_err(&pdev->dev, "AMD GPIO region 0x%x already in use!\n",
  291. + gp.pmbase + PMBASE_OFFSET);
  292. + err = -EBUSY;
  293. + goto out;
  294. + }
  295. +
  296. + printk(KERN_INFO "Mapping region @0x%08x\n", gp.pmbase + PMBASE_OFFSET);
  297. + gp.pm = devm_ioremap(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
  298. + if (!gp.pm) {
  299. + dev_err(&pdev->dev, "Couldn't map io port into io memory\n");
  300. + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
  301. + err = -ENOMEM;
  302. + goto out;
  303. + }
  304. + gp.dev = &pdev->dev;
  305. + gp.chip.dev = &pdev->dev;
  306. +
  307. + spin_lock_init(&gp.lock);
  308. +
  309. + printk(KERN_INFO "AMD SB8XX GPIO detected\n");
  310. + err = gpiochip_add(&gp.chip);
  311. + if (err) {
  312. + printk(KERN_ERR "GPIO registering failed (%d)\n",
  313. + err);
  314. + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
  315. + goto out;
  316. + }
  317. +
  318. +out:
  319. + return err;
  320. +}
  321. +
  322. +static int sb8xx_plat_remove(struct platform_device *pdev)
  323. +{
  324. + printk(KERN_INFO "sb8xx_plat_remove()\n");
  325. + gpiochip_remove(&gp.chip);
  326. + devm_release_mem_region(&pdev->dev, gp.pmbase + PMBASE_OFFSET, PMBASE_SIZE);
  327. + return 0;
  328. +}
  329. +
  330. +static struct of_device_id sb8xx_gpio_of_match[] = {
  331. + { .compatible = "gpio-sb8xx", },
  332. + { /* end of table */ }
  333. +};
  334. +MODULE_DEVICE_TABLE(of, sb8xx_gpio_of_match);
  335. +
  336. +static struct platform_driver sb8xx_gpio_driver = {
  337. + .driver = {
  338. + .name = "gpio-sb8xx",
  339. + .of_match_table = sb8xx_gpio_of_match,
  340. + },
  341. + .probe = sb8xx_plat_probe,
  342. + .remove = sb8xx_plat_remove,
  343. +};
  344. +
  345. +static int __init amd_sb8xx_gpio_init(void)
  346. +{
  347. + return platform_driver_register(&sb8xx_gpio_driver);
  348. +}
  349. +
  350. +static void __exit amd_sb8xx_gpio_exit(void)
  351. +{
  352. + platform_driver_unregister(&sb8xx_gpio_driver);
  353. +}
  354. +
  355. +module_init(amd_sb8xx_gpio_init);
  356. +module_exit(amd_sb8xx_gpio_exit);
  357. +
  358. +MODULE_AUTHOR("Tobias Diedrich");
  359. +MODULE_DESCRIPTION("GPIO driver for AMD SB8XX/SB9XX/A5X/A8X chipsets");
  360. +MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement