1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/net/ethernet/ibm/emac/tah.c |
4 | * |
5 | * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. |
6 | * |
7 | * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. |
8 | * <benh@kernel.crashing.org> |
9 | * |
10 | * Based on the arch/ppc version of the driver: |
11 | * |
12 | * Copyright 2004 MontaVista Software, Inc. |
13 | * Matt Porter <mporter@kernel.crashing.org> |
14 | * |
15 | * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net> |
16 | */ |
17 | #include <linux/mod_devicetable.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/platform_device.h> |
20 | #include <asm/io.h> |
21 | |
22 | #include "emac.h" |
23 | #include "core.h" |
24 | |
25 | int tah_attach(struct platform_device *ofdev, int channel) |
26 | { |
27 | struct tah_instance *dev = platform_get_drvdata(ofdev); |
28 | |
29 | mutex_lock(&dev->lock); |
30 | /* Reset has been done at probe() time... nothing else to do for now */ |
31 | ++dev->users; |
32 | mutex_unlock(&dev->lock); |
33 | |
34 | return 0; |
35 | } |
36 | |
37 | void tah_detach(struct platform_device *ofdev, int channel) |
38 | { |
39 | struct tah_instance *dev = platform_get_drvdata(ofdev); |
40 | |
41 | mutex_lock(&dev->lock); |
42 | --dev->users; |
43 | mutex_unlock(&dev->lock); |
44 | } |
45 | |
46 | void tah_reset(struct platform_device *ofdev) |
47 | { |
48 | struct tah_instance *dev = platform_get_drvdata(ofdev); |
49 | struct tah_regs __iomem *p = dev->base; |
50 | int n; |
51 | |
52 | /* Reset TAH */ |
53 | out_be32(&p->mr, TAH_MR_SR); |
54 | n = 100; |
55 | while ((in_be32(&p->mr) & TAH_MR_SR) && n) |
56 | --n; |
57 | |
58 | if (unlikely(!n)) |
59 | printk(KERN_ERR "%pOF: reset timeout\n" , ofdev->dev.of_node); |
60 | |
61 | /* 10KB TAH TX FIFO accommodates the max MTU of 9000 */ |
62 | out_be32(&p->mr, |
63 | TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP | |
64 | TAH_MR_DIG); |
65 | } |
66 | |
67 | int tah_get_regs_len(struct platform_device *ofdev) |
68 | { |
69 | return sizeof(struct emac_ethtool_regs_subhdr) + |
70 | sizeof(struct tah_regs); |
71 | } |
72 | |
73 | void *tah_dump_regs(struct platform_device *ofdev, void *buf) |
74 | { |
75 | struct tah_instance *dev = platform_get_drvdata(ofdev); |
76 | struct emac_ethtool_regs_subhdr *hdr = buf; |
77 | struct tah_regs *regs = (struct tah_regs *)(hdr + 1); |
78 | |
79 | hdr->version = 0; |
80 | hdr->index = 0; /* for now, are there chips with more than one |
81 | * zmii ? if yes, then we'll add a cell_index |
82 | * like we do for emac |
83 | */ |
84 | memcpy_fromio(regs, dev->base, sizeof(struct tah_regs)); |
85 | return regs + 1; |
86 | } |
87 | |
88 | static int tah_probe(struct platform_device *ofdev) |
89 | { |
90 | struct device_node *np = ofdev->dev.of_node; |
91 | struct tah_instance *dev; |
92 | struct resource regs; |
93 | int rc; |
94 | |
95 | rc = -ENOMEM; |
96 | dev = kzalloc(size: sizeof(struct tah_instance), GFP_KERNEL); |
97 | if (dev == NULL) |
98 | goto err_gone; |
99 | |
100 | mutex_init(&dev->lock); |
101 | dev->ofdev = ofdev; |
102 | |
103 | rc = -ENXIO; |
104 | if (of_address_to_resource(dev: np, index: 0, r: ®s)) { |
105 | printk(KERN_ERR "%pOF: Can't get registers address\n" , np); |
106 | goto err_free; |
107 | } |
108 | |
109 | rc = -ENOMEM; |
110 | dev->base = (struct tah_regs __iomem *)ioremap(offset: regs.start, |
111 | size: sizeof(struct tah_regs)); |
112 | if (dev->base == NULL) { |
113 | printk(KERN_ERR "%pOF: Can't map device registers!\n" , np); |
114 | goto err_free; |
115 | } |
116 | |
117 | platform_set_drvdata(pdev: ofdev, data: dev); |
118 | |
119 | /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ |
120 | tah_reset(ofdev); |
121 | |
122 | printk(KERN_INFO "TAH %pOF initialized\n" , ofdev->dev.of_node); |
123 | wmb(); |
124 | |
125 | return 0; |
126 | |
127 | err_free: |
128 | kfree(objp: dev); |
129 | err_gone: |
130 | return rc; |
131 | } |
132 | |
133 | static void tah_remove(struct platform_device *ofdev) |
134 | { |
135 | struct tah_instance *dev = platform_get_drvdata(pdev: ofdev); |
136 | |
137 | WARN_ON(dev->users != 0); |
138 | |
139 | iounmap(addr: dev->base); |
140 | kfree(objp: dev); |
141 | } |
142 | |
143 | static const struct of_device_id tah_match[] = |
144 | { |
145 | { |
146 | .compatible = "ibm,tah" , |
147 | }, |
148 | /* For backward compat with old DT */ |
149 | { |
150 | .type = "tah" , |
151 | }, |
152 | {}, |
153 | }; |
154 | |
155 | static struct platform_driver tah_driver = { |
156 | .driver = { |
157 | .name = "emac-tah" , |
158 | .of_match_table = tah_match, |
159 | }, |
160 | .probe = tah_probe, |
161 | .remove_new = tah_remove, |
162 | }; |
163 | |
164 | int __init tah_init(void) |
165 | { |
166 | return platform_driver_register(&tah_driver); |
167 | } |
168 | |
169 | void tah_exit(void) |
170 | { |
171 | platform_driver_unregister(&tah_driver); |
172 | } |
173 | |