]>
Commit | Line | Data |
---|---|---|
2155c5a2 JR |
1 | From: Matthew Garrett <mjg59@coreos.com> |
2 | Date: Sat, 18 Apr 2015 08:26:34 -0700 | |
3 | Subject: [PATCH 1/3] libata: Stash initial power management configuration | |
4 | ||
5 | The initial configuration for power management settings may be a result of | |
6 | the firmware using its knowledge of the installed hardware to program | |
7 | reasonable defaults. Stash as much of it as possible for later use. | |
8 | ||
9 | Signed-off-by: Matthew Garrett <mjg59@srcf.ucam.org> | |
10 | --- | |
11 | drivers/ata/acard-ahci.c | 3 +++ | |
12 | drivers/ata/ahci.c | 3 +++ | |
13 | drivers/ata/ahci.h | 7 +++++++ | |
14 | drivers/ata/libahci.c | 46 +++++++++++++++++++++++++++++++++++------- | |
15 | drivers/ata/libahci_platform.c | 4 ++++ | |
16 | drivers/ata/libata-core.c | 19 ++++++++++------- | |
17 | drivers/ata/libata-pmp.c | 2 +- | |
18 | drivers/ata/libata.h | 2 +- | |
19 | drivers/ata/sata_highbank.c | 4 ++++ | |
20 | include/linux/libata.h | 3 +++ | |
21 | 10 files changed, 77 insertions(+), 16 deletions(-) | |
22 | ||
23 | diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c | |
24 | index 12489ce..0029229 100644 | |
25 | --- a/drivers/ata/acard-ahci.c | |
26 | +++ b/drivers/ata/acard-ahci.c | |
27 | @@ -476,6 +476,9 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id | |
28 | ata_port_pbar_desc(ap, AHCI_PCI_BAR, | |
29 | 0x100 + ap->port_no * 0x80, "port"); | |
30 | ||
31 | + rc = ahci_setup_port_privdata(ap); | |
32 | + if (rc) | |
33 | + return rc; | |
34 | /* set initial link pm policy */ | |
35 | /* | |
36 | ap->pm_policy = NOT_AVAILABLE; | |
37 | diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c | |
38 | index c7a92a7..0f875e2 100644 | |
39 | --- a/drivers/ata/ahci.c | |
40 | +++ b/drivers/ata/ahci.c | |
41 | @@ -1436,6 +1436,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |
42 | if (ap->flags & ATA_FLAG_EM) | |
43 | ap->em_message_type = hpriv->em_msg_type; | |
44 | ||
45 | + rc = ahci_setup_port_privdata(ap); | |
46 | + if (rc) | |
47 | + return rc; | |
48 | ||
49 | /* disabled/not-implemented port */ | |
50 | if (!(hpriv->port_map & (1 << i))) | |
51 | diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h | |
52 | index 71262e0..c1a4b6a 100644 | |
53 | --- a/drivers/ata/ahci.h | |
54 | +++ b/drivers/ata/ahci.h | |
55 | @@ -313,6 +313,12 @@ struct ahci_port_priv { | |
56 | /* enclosure management info per PM slot */ | |
57 | struct ahci_em_priv em_priv[EM_MAX_SLOTS]; | |
58 | char *irq_desc; /* desc in /proc/interrupts */ | |
59 | + bool init_alpe; /* alpe enabled by default */ | |
60 | + bool init_asp; /* asp enabled by default */ | |
61 | + bool init_devslp; /* devslp enabled by default */ | |
62 | + u32 init_dito; /* initial dito configuration */ | |
63 | + u32 init_deto; /* initial deto configuration */ | |
64 | + u32 init_mdat; /* initial mdat configuration */ | |
65 | }; | |
66 | ||
67 | struct ahci_host_priv { | |
68 | @@ -371,6 +377,7 @@ extern struct ata_port_operations ahci_platform_ops; | |
69 | extern struct ata_port_operations ahci_pmp_retry_srst_ops; | |
70 | ||
71 | unsigned int ahci_dev_classify(struct ata_port *ap); | |
72 | +int ahci_setup_port_privdata(struct ata_port *ap); | |
73 | void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, | |
74 | u32 opts); | |
75 | void ahci_save_initial_config(struct device *dev, | |
76 | diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c | |
77 | index 61a9c07..f0c7120 100644 | |
78 | --- a/drivers/ata/libahci.c | |
79 | +++ b/drivers/ata/libahci.c | |
80 | @@ -2212,19 +2212,53 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) | |
81 | } | |
82 | #endif | |
83 | ||
84 | +/* | |
85 | + * Allocate port privdata and read back initial power management configuration | |
86 | + */ | |
87 | +int ahci_setup_port_privdata(struct ata_port *ap) | |
88 | +{ | |
89 | + struct ahci_port_priv *pp; | |
90 | + u32 cmd, devslp; | |
91 | + void __iomem *port_mmio = ahci_port_base(ap); | |
92 | + | |
93 | + pp = kzalloc(sizeof(*pp), GFP_KERNEL); | |
94 | + if (!pp) | |
95 | + return -ENOMEM; | |
96 | + | |
97 | + ap->private_data = pp; | |
98 | + | |
99 | + cmd = readl(port_mmio + PORT_CMD); | |
100 | + | |
101 | + if (cmd & PORT_CMD_ALPE) | |
102 | + pp->init_alpe = true; | |
103 | + | |
104 | + if (cmd & PORT_CMD_ASP) | |
105 | + pp->init_asp = true; | |
106 | + | |
107 | + devslp = readl(port_mmio + PORT_DEVSLP); | |
108 | + | |
109 | + /* devslp unsupported or disabled */ | |
110 | + if (!(devslp & PORT_DEVSLP_DSP) || !(devslp & PORT_DEVSLP_ADSE)) | |
111 | + return 0; | |
112 | + | |
113 | + pp->init_devslp = true; | |
114 | + pp->init_dito = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3ff; | |
115 | + pp->init_deto = (devslp >> PORT_DEVSLP_DETO_OFFSET) & 0xff; | |
116 | + pp->init_mdat = (devslp >> PORT_DEVSLP_MDAT_OFFSET) & 0x1f; | |
117 | + | |
118 | + return 0; | |
119 | +} | |
120 | +EXPORT_SYMBOL_GPL(ahci_setup_port_privdata); | |
121 | + | |
122 | static int ahci_port_start(struct ata_port *ap) | |
123 | { | |
124 | struct ahci_host_priv *hpriv = ap->host->private_data; | |
125 | + struct ahci_port_priv *pp = ap->private_data; | |
126 | struct device *dev = ap->host->dev; | |
127 | - struct ahci_port_priv *pp; | |
128 | void *mem; | |
129 | dma_addr_t mem_dma; | |
130 | size_t dma_sz, rx_fis_sz; | |
131 | ||
132 | - pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); | |
133 | - if (!pp) | |
134 | - return -ENOMEM; | |
135 | - | |
136 | if (ap->host->n_ports > 1) { | |
137 | pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL); | |
138 | if (!pp->irq_desc) { | |
139 | @@ -2303,8 +2337,6 @@ static int ahci_port_start(struct ata_port *ap) | |
140 | ap->lock = &pp->lock; | |
141 | } | |
142 | ||
143 | - ap->private_data = pp; | |
144 | - | |
145 | /* engage engines, captain */ | |
146 | return ahci_port_resume(ap); | |
147 | } | |
148 | diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c | |
149 | index d89305d..39946d4 100644 | |
150 | --- a/drivers/ata/libahci_platform.c | |
151 | +++ b/drivers/ata/libahci_platform.c | |
152 | @@ -563,6 +563,10 @@ int ahci_platform_init_host(struct platform_device *pdev, | |
153 | if (ap->flags & ATA_FLAG_EM) | |
154 | ap->em_message_type = hpriv->em_msg_type; | |
155 | ||
156 | + rc = ahci_setup_port_privdata(ap); | |
157 | + if (rc) | |
158 | + return rc; | |
159 | + | |
160 | /* disabled/not-implemented port */ | |
161 | if (!(hpriv->port_map & (1 << i))) | |
162 | ap->ops = &ata_dummy_port_ops; | |
163 | diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c | |
164 | index f6cb1f1..c037f33 100644 | |
165 | --- a/drivers/ata/libata-core.c | |
166 | +++ b/drivers/ata/libata-core.c | |
167 | @@ -2024,6 +2024,9 @@ retry: | |
168 | } | |
169 | } | |
170 | ||
171 | + if (id[79] & SATA_DIPM) | |
172 | + dev->init_dipm = true; | |
173 | + | |
174 | *p_class = class; | |
175 | ||
176 | return 0; | |
177 | @@ -5583,11 +5586,11 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) | |
178 | } | |
179 | ||
180 | /** | |
181 | - * sata_link_init_spd - Initialize link->sata_spd_limit | |
182 | - * @link: Link to configure sata_spd_limit for | |
183 | + * sata_link_init_config - Initialize link->sata_spd_limit and init_lpm | |
184 | + * @link: Link to configure sata_spd_limit and init_lpm for | |
185 | * | |
186 | - * Initialize @link->[hw_]sata_spd_limit to the currently | |
187 | - * configured value. | |
188 | + * Initialize @link->[hw_]sata_spd_limit and @link->init_lpm to the | |
189 | + * currently configured value. | |
190 | * | |
191 | * LOCKING: | |
192 | * Kernel thread context (may sleep). | |
193 | @@ -5595,7 +5598,7 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) | |
194 | * RETURNS: | |
195 | * 0 on success, -errno on failure. | |
196 | */ | |
197 | -int sata_link_init_spd(struct ata_link *link) | |
198 | +int sata_link_init_config(struct ata_link *link) | |
199 | { | |
200 | u8 spd; | |
201 | int rc; | |
202 | @@ -5612,6 +5615,8 @@ int sata_link_init_spd(struct ata_link *link) | |
203 | ||
204 | link->sata_spd_limit = link->hw_sata_spd_limit; | |
205 | ||
206 | + link->init_lpm = (link->saved_scontrol >> 8) & 0x7; | |
207 | + | |
208 | return 0; | |
209 | } | |
210 | ||
211 | @@ -6161,9 +6166,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) | |
212 | ap->cbl = ATA_CBL_SATA; | |
213 | ||
214 | /* init sata_spd_limit to the current value */ | |
215 | - sata_link_init_spd(&ap->link); | |
216 | + sata_link_init_config(&ap->link); | |
217 | if (ap->slave_link) | |
218 | - sata_link_init_spd(ap->slave_link); | |
219 | + sata_link_init_config(ap->slave_link); | |
220 | ||
221 | /* print per-port info to dmesg */ | |
222 | xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, | |
223 | diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c | |
224 | index 7ccc084..b9f7ce8 100644 | |
225 | --- a/drivers/ata/libata-pmp.c | |
226 | +++ b/drivers/ata/libata-pmp.c | |
227 | @@ -531,7 +531,7 @@ int sata_pmp_attach(struct ata_device *dev) | |
228 | ap->ops->pmp_attach(ap); | |
229 | ||
230 | ata_for_each_link(tlink, ap, EDGE) | |
231 | - sata_link_init_spd(tlink); | |
232 | + sata_link_init_config(tlink); | |
233 | ||
234 | return 0; | |
235 | ||
236 | diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h | |
237 | index a998a17..35016d6 100644 | |
238 | --- a/drivers/ata/libata.h | |
239 | +++ b/drivers/ata/libata.h | |
240 | @@ -99,7 +99,7 @@ extern bool ata_phys_link_online(struct ata_link *link); | |
241 | extern bool ata_phys_link_offline(struct ata_link *link); | |
242 | extern void ata_dev_init(struct ata_device *dev); | |
243 | extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp); | |
244 | -extern int sata_link_init_spd(struct ata_link *link); | |
245 | +extern int sata_link_init_config(struct ata_link *link); | |
246 | extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); | |
247 | extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); | |
248 | extern struct ata_port *ata_port_alloc(struct ata_host *host); | |
249 | diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c | |
250 | index 24e311f..a2adf3f 100644 | |
251 | --- a/drivers/ata/sata_highbank.c | |
252 | +++ b/drivers/ata/sata_highbank.c | |
253 | @@ -556,6 +556,10 @@ static int ahci_highbank_probe(struct platform_device *pdev) | |
254 | if (ap->flags & ATA_FLAG_EM) | |
255 | ap->em_message_type = hpriv->em_msg_type; | |
256 | ||
257 | + rc = ahci_setup_port_privdata(ap); | |
258 | + if (rc) | |
259 | + goto err0; | |
260 | + | |
261 | /* disabled/not-implemented port */ | |
262 | if (!(hpriv->port_map & (1 << i))) | |
263 | ap->ops = &ata_dummy_port_ops; | |
264 | diff --git a/include/linux/libata.h b/include/linux/libata.h | |
265 | index 8dad4a3..31c149b 100644 | |
266 | --- a/include/linux/libata.h | |
267 | +++ b/include/linux/libata.h | |
268 | @@ -719,6 +719,8 @@ struct ata_device { | |
269 | int spdn_cnt; | |
270 | /* ering is CLEAR_END, read comment above CLEAR_END */ | |
271 | struct ata_ering ering; | |
272 | + /* Initial DIPM configuration */ | |
273 | + bool init_dipm; | |
274 | }; | |
275 | ||
276 | /* Fields between ATA_DEVICE_CLEAR_BEGIN and ATA_DEVICE_CLEAR_END are | |
b912730e | 277 | @@ -800,6 +800,7 @@ struct ata_link { |
2155c5a2 | 278 | struct ata_device device[ATA_MAX_DEVICES]; |
b912730e AM |
279 | |
280 | unsigned long last_lpm_change; /* when last LPM change happened */ | |
2155c5a2 JR |
281 | + u8 init_lpm; /* initial lpm configuration */ |
282 | }; | |
283 | #define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag) | |
284 | #define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0]) | |
285 | -- | |
286 | 2.3.5 | |
287 | ||
288 | -- | |
289 | To unsubscribe from this list: send the line "unsubscribe linux-kernel" in | |
290 | the body of a message to majordomo@vger.kernel.org | |
291 | More majordomo info at http://vger.kernel.org/majordomo-info.html | |
292 | Please read the FAQ at http://www.tux.org/lkml/ | |
293 | From: Matthew Garrett <mjg59@coreos.com> | |
294 | Date: Sat, 18 Apr 2015 08:26:35 -0700 | |
295 | Subject: [PATCH 2/3] libata: Add firmware_default LPM policy | |
296 | ||
297 | System vendors may configure SATA link power management appropriately for | |
298 | their systems in firmware, based on their knowledge of the hardware | |
299 | installed. Add an additional LPM policy designed to inherit the | |
300 | configuration provided by the firmware. | |
301 | ||
302 | Signed-off-by: Matthew Garrett <mjg59@coreos.com> | |
303 | --- | |
304 | .../scsi/link_power_management_policy.txt | 5 +- | |
305 | drivers/ata/libahci.c | 62 +++++++++++++++------- | |
306 | drivers/ata/libata-core.c | 7 ++- | |
307 | drivers/ata/libata-eh.c | 15 +++--- | |
308 | drivers/ata/libata-scsi.c | 1 + | |
309 | include/linux/libata.h | 1 + | |
310 | 6 files changed, 63 insertions(+), 28 deletions(-) | |
311 | ||
312 | diff --git a/Documentation/scsi/link_power_management_policy.txt b/Documentation/scsi/link_power_management_policy.txt | |
313 | index d18993d..0285601 100644 | |
314 | --- a/Documentation/scsi/link_power_management_policy.txt | |
315 | +++ b/Documentation/scsi/link_power_management_policy.txt | |
316 | @@ -1,8 +1,11 @@ | |
317 | This parameter allows the user to set the link (interface) power management. | |
318 | -There are 3 possible options: | |
319 | +There are 4 possible options: | |
320 | ||
321 | Value Effect | |
322 | ---------------------------------------------------------------------------- | |
323 | +firmware_defaults Inherit configuration from the state programmed by | |
324 | + the firmware during system init. | |
325 | + | |
326 | min_power Tell the controller to try to make the link use the | |
327 | least possible power when possible. This may | |
328 | sacrifice some performance due to increased latency | |
329 | diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c | |
330 | index f0c7120..fabcff4 100644 | |
331 | --- a/drivers/ata/libahci.c | |
332 | +++ b/drivers/ata/libahci.c | |
333 | @@ -684,6 +684,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
334 | { | |
335 | struct ata_port *ap = link->ap; | |
336 | struct ahci_host_priv *hpriv = ap->host->private_data; | |
337 | + struct ahci_port_priv *ppriv = ap->private_data; | |
338 | struct ahci_port_priv *pp = ap->private_data; | |
339 | void __iomem *port_mmio = ahci_port_base(ap); | |
340 | ||
5076564f | 341 | @@ -701,10 +702,10 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, |
2155c5a2 JR |
342 | |
343 | if (hpriv->cap & HOST_CAP_ALPM) { | |
344 | u32 cmd = readl(port_mmio + PORT_CMD); | |
5076564f JR |
345 | + if (!(hints & ATA_LPM_WAKE_ONLY)) |
346 | + cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); | |
2155c5a2 JR |
347 | |
348 | if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { | |
5076564f JR |
349 | - if (!(hints & ATA_LPM_WAKE_ONLY)) |
350 | - cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); | |
2155c5a2 JR |
351 | cmd |= PORT_CMD_ICC_ACTIVE; |
352 | ||
353 | writel(cmd, port_mmio + PORT_CMD); | |
354 | @@ -711,6 +712,13 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
355 | ||
5076564f JR |
356 | if (hints & ATA_LPM_WAKE_ONLY) |
357 | return 0; | |
2155c5a2 JR |
358 | + } else if (policy == ATA_LPM_FIRMWARE_DEFAULTS) { |
359 | + if (ppriv->init_alpe) | |
360 | + cmd |= PORT_CMD_ALPE; | |
361 | + if (ppriv->init_asp) | |
362 | + cmd |= PORT_CMD_ASP; | |
363 | + | |
364 | + writel(cmd, port_mmio + PORT_CMD); | |
365 | } else { | |
366 | cmd |= PORT_CMD_ALPE; | |
367 | if (policy == ATA_LPM_MIN_POWER) | |
368 | @@ -725,10 +733,17 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
369 | if ((hpriv->cap2 & HOST_CAP2_SDS) && | |
370 | (hpriv->cap2 & HOST_CAP2_SADM) && | |
371 | (link->device->flags & ATA_DFLAG_DEVSLP)) { | |
372 | - if (policy == ATA_LPM_MIN_POWER) | |
373 | + switch (policy) { | |
374 | + case ATA_LPM_MIN_POWER: | |
375 | ahci_set_aggressive_devslp(ap, true); | |
376 | - else | |
377 | + break; | |
378 | + case ATA_LPM_FIRMWARE_DEFAULTS: | |
379 | + ahci_set_aggressive_devslp(ap, ppriv->init_devslp); | |
380 | + break; | |
381 | + default: | |
382 | ahci_set_aggressive_devslp(ap, false); | |
383 | + break; | |
384 | + } | |
385 | } | |
386 | ||
387 | if (policy == ATA_LPM_MAX_POWER) { | |
388 | @@ -1995,6 +2010,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) | |
389 | static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) | |
390 | { | |
391 | struct ahci_host_priv *hpriv = ap->host->private_data; | |
392 | + struct ahci_port_priv *ppriv = ap->private_data; | |
393 | void __iomem *port_mmio = ahci_port_base(ap); | |
394 | struct ata_device *dev = ap->link.device; | |
395 | u32 devslp, dm, dito, mdat, deto; | |
396 | @@ -2030,26 +2046,32 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) | |
397 | if (rc) | |
398 | return; | |
399 | ||
400 | - dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; | |
401 | - dito = devslp_idle_timeout / (dm + 1); | |
402 | - if (dito > 0x3ff) | |
403 | - dito = 0x3ff; | |
404 | + if (ppriv->init_devslp) { | |
405 | + dito = ppriv->init_dito; | |
406 | + deto = ppriv->init_deto; | |
407 | + mdat = ppriv->init_mdat; | |
408 | + } else { | |
409 | + dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; | |
410 | + dito = devslp_idle_timeout / (dm + 1); | |
411 | + if (dito > 0x3ff) | |
412 | + dito = 0x3ff; | |
413 | ||
414 | - /* Use the nominal value 10 ms if the read MDAT is zero, | |
415 | - * the nominal value of DETO is 20 ms. | |
416 | - */ | |
417 | - if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & | |
418 | - ATA_LOG_DEVSLP_VALID_MASK) { | |
419 | - mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & | |
420 | - ATA_LOG_DEVSLP_MDAT_MASK; | |
421 | - if (!mdat) | |
422 | + /* Use the nominal value 10 ms if the read MDAT is zero, | |
423 | + * the nominal value of DETO is 20 ms. | |
424 | + */ | |
425 | + if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & | |
426 | + ATA_LOG_DEVSLP_VALID_MASK) { | |
427 | + mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & | |
428 | + ATA_LOG_DEVSLP_MDAT_MASK; | |
429 | + if (!mdat) | |
430 | + mdat = 10; | |
431 | + deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; | |
432 | + if (!deto) | |
433 | + deto = 20; | |
434 | + } else { | |
435 | mdat = 10; | |
436 | - deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; | |
437 | - if (!deto) | |
438 | deto = 20; | |
439 | - } else { | |
440 | - mdat = 10; | |
441 | - deto = 20; | |
442 | + } | |
443 | } | |
444 | ||
445 | devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) | | |
446 | diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c | |
447 | index c037f33..0a78f01 100644 | |
448 | --- a/drivers/ata/libata-core.c | |
449 | +++ b/drivers/ata/libata-core.c | |
450 | @@ -2024,7 +2024,7 @@ retry: | |
451 | } | |
452 | } | |
453 | ||
454 | - if (id[79] & SATA_DIPM) | |
455 | + if (id[79] & (1 << SATA_DIPM)) | |
456 | dev->init_dipm = true; | |
457 | ||
458 | *p_class = class; | |
459 | @@ -3672,6 +3672,11 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
460 | return rc; | |
461 | ||
462 | switch (policy) { | |
463 | + case ATA_LPM_FIRMWARE_DEFAULTS: | |
464 | + /* use the values we read at probe */ | |
465 | + scontrol &= ~(0x7 << 8); | |
466 | + scontrol |= (link->init_lpm << 8); | |
467 | + break; | |
468 | case ATA_LPM_MAX_POWER: | |
469 | /* disable all LPM transitions */ | |
470 | scontrol |= (0x7 << 8); | |
471 | diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c | |
472 | index 07f41be..c36fa56 100644 | |
473 | --- a/drivers/ata/libata-eh.c | |
474 | +++ b/drivers/ata/libata-eh.c | |
475 | @@ -3519,9 +3519,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
476 | return 0; | |
477 | ||
478 | /* | |
479 | - * DIPM is enabled only for MIN_POWER as some devices | |
480 | - * misbehave when the host NACKs transition to SLUMBER. Order | |
481 | - * device and link configurations such that the host always | |
482 | + * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some | |
483 | + * devices misbehave when the host NACKs transition to SLUMBER. | |
484 | + * Order device and link configurations such that the host always | |
485 | * allows DIPM requests. | |
486 | */ | |
487 | ata_for_each_dev(dev, link, ENABLED) { | |
488 | @@ -3581,10 +3581,13 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
489 | if (ap && ap->slave_link) | |
490 | ap->slave_link->lpm_policy = policy; | |
491 | ||
492 | - /* host config updated, enable DIPM if transitioning to MIN_POWER */ | |
493 | + /* host config updated, enable DIPM if transitioning to MIN_POWER or | |
494 | + * FIRMWARE_DEFAULT when enabled by firmware | |
495 | + */ | |
496 | ata_for_each_dev(dev, link, ENABLED) { | |
497 | - if (policy == ATA_LPM_MIN_POWER && !no_dipm && | |
498 | - ata_id_has_dipm(dev->id)) { | |
499 | + if ((policy == ATA_LPM_MIN_POWER && !no_dipm && | |
500 | + ata_id_has_dipm(dev->id)) || | |
501 | + (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) { | |
502 | err_mask = ata_dev_set_feature(dev, | |
503 | SETFEATURES_SATA_ENABLE, SATA_DIPM); | |
504 | if (err_mask && err_mask != AC_ERR_DEV) { | |
505 | diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c | |
506 | index 3131adc..f1ea052 100644 | |
507 | --- a/drivers/ata/libata-scsi.c | |
508 | +++ b/drivers/ata/libata-scsi.c | |
509 | @@ -107,6 +107,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { | |
510 | static const char *ata_lpm_policy_names[] = { | |
511 | [ATA_LPM_UNKNOWN] = "max_performance", | |
512 | [ATA_LPM_MAX_POWER] = "max_performance", | |
513 | + [ATA_LPM_FIRMWARE_DEFAULTS] = "firmware_defaults", | |
514 | [ATA_LPM_MED_POWER] = "medium_power", | |
515 | [ATA_LPM_MIN_POWER] = "min_power", | |
516 | }; | |
517 | diff --git a/include/linux/libata.h b/include/linux/libata.h | |
518 | index 31c149b..57b465d 100644 | |
519 | --- a/include/linux/libata.h | |
520 | +++ b/include/linux/libata.h | |
521 | @@ -507,6 +507,7 @@ enum ata_completion_errors { | |
522 | enum ata_lpm_policy { | |
523 | ATA_LPM_UNKNOWN, | |
524 | ATA_LPM_MAX_POWER, | |
525 | + ATA_LPM_FIRMWARE_DEFAULTS, | |
526 | ATA_LPM_MED_POWER, | |
527 | ATA_LPM_MIN_POWER, | |
528 | }; | |
529 | -- | |
530 | 2.3.5 | |
531 | ||
532 | -- | |
533 | To unsubscribe from this list: send the line "unsubscribe linux-kernel" in | |
534 | the body of a message to majordomo@vger.kernel.org | |
535 | More majordomo info at http://vger.kernel.org/majordomo-info.html | |
536 | Please read the FAQ at http://www.tux.org/lkml/ | |
537 | From: Matthew Garrett <mjg59@coreos.com> | |
538 | Date: Sat, 18 Apr 2015 08:26:36 -0700 | |
539 | Subject: [PATCH 3/3] libata: Change medium_power LPM policy to match Intel recommendations | |
540 | ||
541 | Intel publish a document on designing energy efficient SATA devices at | |
542 | http://www.intel.com/content/dam/doc/reference-guide/sata-devices-implementation-recommendations.pdf | |
543 | which recommends that ALPE be set, ASPE be cleared and that DIPM be enabled | |
544 | on the device. Right now we have no policy that matches that - medium_power | |
545 | does not enable DIPM and min_power sets ASPE. Change medium_power to | |
546 | implement these recommendations, with the addition of devslp state being | |
547 | inherited from the initial configuration. With luck this will provide | |
548 | reasonable power savings without causing the device breakages we | |
549 | occasionally see with the min_power policy. | |
550 | ||
551 | Signed-off-by: Matthew Garrett <mjg59@coreos.com> | |
552 | --- | |
553 | drivers/ata/libahci.c | 1 + | |
554 | drivers/ata/libata-core.c | 4 ---- | |
555 | drivers/ata/libata-eh.c | 10 ++++------ | |
556 | 3 files changed, 5 insertions(+), 10 deletions(-) | |
557 | ||
558 | diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c | |
559 | index fabcff4..8efacb9 100644 | |
560 | --- a/drivers/ata/libahci.c | |
561 | +++ b/drivers/ata/libahci.c | |
562 | @@ -738,6 +738,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
563 | ahci_set_aggressive_devslp(ap, true); | |
564 | break; | |
565 | case ATA_LPM_FIRMWARE_DEFAULTS: | |
566 | + case ATA_LPM_MED_POWER: | |
567 | ahci_set_aggressive_devslp(ap, ppriv->init_devslp); | |
568 | break; | |
569 | default: | |
570 | diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c | |
571 | index 0a78f01..99a7b8f 100644 | |
572 | --- a/drivers/ata/libata-core.c | |
573 | +++ b/drivers/ata/libata-core.c | |
574 | @@ -3687,10 +3687,6 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
575 | } | |
576 | break; | |
577 | case ATA_LPM_MED_POWER: | |
578 | - /* allow LPM to PARTIAL */ | |
579 | - scontrol &= ~(0x1 << 8); | |
580 | - scontrol |= (0x6 << 8); | |
581 | - break; | |
582 | case ATA_LPM_MIN_POWER: | |
583 | if (ata_link_nr_enabled(link) > 0) | |
584 | /* no restrictions on LPM transitions */ | |
585 | diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c | |
586 | index c36fa56..25d5f37 100644 | |
587 | --- a/drivers/ata/libata-eh.c | |
588 | +++ b/drivers/ata/libata-eh.c | |
589 | @@ -3519,8 +3519,6 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
590 | return 0; | |
591 | ||
592 | /* | |
593 | - * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some | |
594 | - * devices misbehave when the host NACKs transition to SLUMBER. | |
595 | * Order device and link configurations such that the host always | |
596 | * allows DIPM requests. | |
597 | */ | |
598 | @@ -3540,7 +3538,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
599 | hints &= ~ATA_LPM_HIPM; | |
600 | ||
601 | /* disable DIPM before changing link config */ | |
602 | - if (policy != ATA_LPM_MIN_POWER && dipm) { | |
603 | + if (policy < ATA_LPM_MED_POWER && dipm) { | |
604 | err_mask = ata_dev_set_feature(dev, | |
605 | SETFEATURES_SATA_DISABLE, SATA_DIPM); | |
606 | if (err_mask && err_mask != AC_ERR_DEV) { | |
607 | @@ -3581,11 +3579,11 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, | |
608 | if (ap && ap->slave_link) | |
609 | ap->slave_link->lpm_policy = policy; | |
610 | ||
611 | - /* host config updated, enable DIPM if transitioning to MIN_POWER or | |
612 | - * FIRMWARE_DEFAULT when enabled by firmware | |
613 | + /* host config updated, enable DIPM if transitioning to MED_POWER, | |
614 | + * MIN_POWER or FIRMWARE_DEFAULT when enabled by firmware | |
615 | */ | |
616 | ata_for_each_dev(dev, link, ENABLED) { | |
617 | - if ((policy == ATA_LPM_MIN_POWER && !no_dipm && | |
618 | + if ((policy >= ATA_LPM_MED_POWER && !no_dipm && | |
619 | ata_id_has_dipm(dev->id)) || | |
620 | (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) { | |
621 | err_mask = ata_dev_set_feature(dev, | |
622 | -- | |
623 | 2.3.5 | |
624 | ||
625 | -- | |
626 | To unsubscribe from this list: send the line "unsubscribe linux-kernel" in | |
627 | the body of a message to majordomo@vger.kernel.org | |
628 | More majordomo info at http://vger.kernel.org/majordomo-info.html | |
629 | Please read the FAQ at http://www.tux.org/lkml/ |