1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2011 Intel Corporation
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice (including the next
12*4882a593Smuzhiyun * paragraph) shall be included in all copies or substantial portions of the
13*4882a593Smuzhiyun * Software.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18*4882a593Smuzhiyun * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*4882a593Smuzhiyun * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*4882a593Smuzhiyun * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21*4882a593Smuzhiyun * DEALINGS IN THE SOFTWARE.
22*4882a593Smuzhiyun *
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun #include <linux/acpi.h>
25*4882a593Smuzhiyun #include "psb_drv.h"
26*4882a593Smuzhiyun #include "psb_intel_reg.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define PCI_ASLE 0xe4
29*4882a593Smuzhiyun #define PCI_ASLS 0xfc
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #define OPREGION_HEADER_OFFSET 0
32*4882a593Smuzhiyun #define OPREGION_ACPI_OFFSET 0x100
33*4882a593Smuzhiyun #define ACPI_CLID 0x01ac /* current lid state indicator */
34*4882a593Smuzhiyun #define ACPI_CDCK 0x01b0 /* current docking state indicator */
35*4882a593Smuzhiyun #define OPREGION_SWSCI_OFFSET 0x200
36*4882a593Smuzhiyun #define OPREGION_ASLE_OFFSET 0x300
37*4882a593Smuzhiyun #define OPREGION_VBT_OFFSET 0x400
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define OPREGION_SIGNATURE "IntelGraphicsMem"
40*4882a593Smuzhiyun #define MBOX_ACPI (1<<0)
41*4882a593Smuzhiyun #define MBOX_SWSCI (1<<1)
42*4882a593Smuzhiyun #define MBOX_ASLE (1<<2)
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun struct opregion_header {
45*4882a593Smuzhiyun u8 signature[16];
46*4882a593Smuzhiyun u32 size;
47*4882a593Smuzhiyun u32 opregion_ver;
48*4882a593Smuzhiyun u8 bios_ver[32];
49*4882a593Smuzhiyun u8 vbios_ver[16];
50*4882a593Smuzhiyun u8 driver_ver[16];
51*4882a593Smuzhiyun u32 mboxes;
52*4882a593Smuzhiyun u8 reserved[164];
53*4882a593Smuzhiyun } __packed;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun /* OpRegion mailbox #1: public ACPI methods */
56*4882a593Smuzhiyun struct opregion_acpi {
57*4882a593Smuzhiyun u32 drdy; /* driver readiness */
58*4882a593Smuzhiyun u32 csts; /* notification status */
59*4882a593Smuzhiyun u32 cevt; /* current event */
60*4882a593Smuzhiyun u8 rsvd1[20];
61*4882a593Smuzhiyun u32 didl[8]; /* supported display devices ID list */
62*4882a593Smuzhiyun u32 cpdl[8]; /* currently presented display list */
63*4882a593Smuzhiyun u32 cadl[8]; /* currently active display list */
64*4882a593Smuzhiyun u32 nadl[8]; /* next active devices list */
65*4882a593Smuzhiyun u32 aslp; /* ASL sleep time-out */
66*4882a593Smuzhiyun u32 tidx; /* toggle table index */
67*4882a593Smuzhiyun u32 chpd; /* current hotplug enable indicator */
68*4882a593Smuzhiyun u32 clid; /* current lid state*/
69*4882a593Smuzhiyun u32 cdck; /* current docking state */
70*4882a593Smuzhiyun u32 sxsw; /* Sx state resume */
71*4882a593Smuzhiyun u32 evts; /* ASL supported events */
72*4882a593Smuzhiyun u32 cnot; /* current OS notification */
73*4882a593Smuzhiyun u32 nrdy; /* driver status */
74*4882a593Smuzhiyun u8 rsvd2[60];
75*4882a593Smuzhiyun } __packed;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /* OpRegion mailbox #2: SWSCI */
78*4882a593Smuzhiyun struct opregion_swsci {
79*4882a593Smuzhiyun /*FIXME: add it later*/
80*4882a593Smuzhiyun } __packed;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun /* OpRegion mailbox #3: ASLE */
83*4882a593Smuzhiyun struct opregion_asle {
84*4882a593Smuzhiyun u32 ardy; /* driver readiness */
85*4882a593Smuzhiyun u32 aslc; /* ASLE interrupt command */
86*4882a593Smuzhiyun u32 tche; /* technology enabled indicator */
87*4882a593Smuzhiyun u32 alsi; /* current ALS illuminance reading */
88*4882a593Smuzhiyun u32 bclp; /* backlight brightness to set */
89*4882a593Smuzhiyun u32 pfit; /* panel fitting state */
90*4882a593Smuzhiyun u32 cblv; /* current brightness level */
91*4882a593Smuzhiyun u16 bclm[20]; /* backlight level duty cycle mapping table */
92*4882a593Smuzhiyun u32 cpfm; /* current panel fitting mode */
93*4882a593Smuzhiyun u32 epfm; /* enabled panel fitting modes */
94*4882a593Smuzhiyun u8 plut[74]; /* panel LUT and identifier */
95*4882a593Smuzhiyun u32 pfmb; /* PWM freq and min brightness */
96*4882a593Smuzhiyun u8 rsvd[102];
97*4882a593Smuzhiyun } __packed;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun /* ASLE irq request bits */
100*4882a593Smuzhiyun #define ASLE_SET_ALS_ILLUM (1 << 0)
101*4882a593Smuzhiyun #define ASLE_SET_BACKLIGHT (1 << 1)
102*4882a593Smuzhiyun #define ASLE_SET_PFIT (1 << 2)
103*4882a593Smuzhiyun #define ASLE_SET_PWM_FREQ (1 << 3)
104*4882a593Smuzhiyun #define ASLE_REQ_MSK 0xf
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /* response bits of ASLE irq request */
107*4882a593Smuzhiyun #define ASLE_ALS_ILLUM_FAILED (1<<10)
108*4882a593Smuzhiyun #define ASLE_BACKLIGHT_FAILED (1<<12)
109*4882a593Smuzhiyun #define ASLE_PFIT_FAILED (1<<14)
110*4882a593Smuzhiyun #define ASLE_PWM_FREQ_FAILED (1<<16)
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun /* ASLE backlight brightness to set */
113*4882a593Smuzhiyun #define ASLE_BCLP_VALID (1<<31)
114*4882a593Smuzhiyun #define ASLE_BCLP_MSK (~(1<<31))
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* ASLE panel fitting request */
117*4882a593Smuzhiyun #define ASLE_PFIT_VALID (1<<31)
118*4882a593Smuzhiyun #define ASLE_PFIT_CENTER (1<<0)
119*4882a593Smuzhiyun #define ASLE_PFIT_STRETCH_TEXT (1<<1)
120*4882a593Smuzhiyun #define ASLE_PFIT_STRETCH_GFX (1<<2)
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* response bits of ASLE irq request */
123*4882a593Smuzhiyun #define ASLE_ALS_ILLUM_FAILED (1<<10)
124*4882a593Smuzhiyun #define ASLE_BACKLIGHT_FAILED (1<<12)
125*4882a593Smuzhiyun #define ASLE_PFIT_FAILED (1<<14)
126*4882a593Smuzhiyun #define ASLE_PWM_FREQ_FAILED (1<<16)
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /* ASLE backlight brightness to set */
129*4882a593Smuzhiyun #define ASLE_BCLP_VALID (1<<31)
130*4882a593Smuzhiyun #define ASLE_BCLP_MSK (~(1<<31))
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun /* ASLE panel fitting request */
133*4882a593Smuzhiyun #define ASLE_PFIT_VALID (1<<31)
134*4882a593Smuzhiyun #define ASLE_PFIT_CENTER (1<<0)
135*4882a593Smuzhiyun #define ASLE_PFIT_STRETCH_TEXT (1<<1)
136*4882a593Smuzhiyun #define ASLE_PFIT_STRETCH_GFX (1<<2)
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun /* PWM frequency and minimum brightness */
139*4882a593Smuzhiyun #define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
140*4882a593Smuzhiyun #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
141*4882a593Smuzhiyun #define ASLE_PFMB_PWM_MASK (0x7ffffe00)
142*4882a593Smuzhiyun #define ASLE_PFMB_PWM_VALID (1<<31)
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun #define ASLE_CBLV_VALID (1<<31)
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun static struct psb_intel_opregion *system_opregion;
147*4882a593Smuzhiyun
asle_set_backlight(struct drm_device * dev,u32 bclp)148*4882a593Smuzhiyun static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
151*4882a593Smuzhiyun struct opregion_asle *asle = dev_priv->opregion.asle;
152*4882a593Smuzhiyun struct backlight_device *bd = dev_priv->backlight_device;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun DRM_DEBUG_DRIVER("asle set backlight %x\n", bclp);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (!(bclp & ASLE_BCLP_VALID))
157*4882a593Smuzhiyun return ASLE_BACKLIGHT_FAILED;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (bd == NULL)
160*4882a593Smuzhiyun return ASLE_BACKLIGHT_FAILED;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun bclp &= ASLE_BCLP_MSK;
163*4882a593Smuzhiyun if (bclp > 255)
164*4882a593Smuzhiyun return ASLE_BACKLIGHT_FAILED;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun gma_backlight_set(dev, bclp * bd->props.max_brightness / 255);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
psb_intel_opregion_asle_work(struct work_struct * work)173*4882a593Smuzhiyun static void psb_intel_opregion_asle_work(struct work_struct *work)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun struct psb_intel_opregion *opregion =
176*4882a593Smuzhiyun container_of(work, struct psb_intel_opregion, asle_work);
177*4882a593Smuzhiyun struct drm_psb_private *dev_priv =
178*4882a593Smuzhiyun container_of(opregion, struct drm_psb_private, opregion);
179*4882a593Smuzhiyun struct opregion_asle *asle = opregion->asle;
180*4882a593Smuzhiyun u32 asle_stat = 0;
181*4882a593Smuzhiyun u32 asle_req;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun if (!asle)
184*4882a593Smuzhiyun return;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun asle_req = asle->aslc & ASLE_REQ_MSK;
187*4882a593Smuzhiyun if (!asle_req) {
188*4882a593Smuzhiyun DRM_DEBUG_DRIVER("non asle set request??\n");
189*4882a593Smuzhiyun return;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun if (asle_req & ASLE_SET_BACKLIGHT)
193*4882a593Smuzhiyun asle_stat |= asle_set_backlight(dev_priv->dev, asle->bclp);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun asle->aslc = asle_stat;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
psb_intel_opregion_asle_intr(struct drm_device * dev)199*4882a593Smuzhiyun void psb_intel_opregion_asle_intr(struct drm_device *dev)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (dev_priv->opregion.asle)
204*4882a593Smuzhiyun schedule_work(&dev_priv->opregion.asle_work);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun #define ASLE_ALS_EN (1<<0)
208*4882a593Smuzhiyun #define ASLE_BLC_EN (1<<1)
209*4882a593Smuzhiyun #define ASLE_PFIT_EN (1<<2)
210*4882a593Smuzhiyun #define ASLE_PFMB_EN (1<<3)
211*4882a593Smuzhiyun
psb_intel_opregion_enable_asle(struct drm_device * dev)212*4882a593Smuzhiyun void psb_intel_opregion_enable_asle(struct drm_device *dev)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
215*4882a593Smuzhiyun struct opregion_asle *asle = dev_priv->opregion.asle;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun if (asle && system_opregion ) {
218*4882a593Smuzhiyun /* Don't do this on Medfield or other non PC like devices, they
219*4882a593Smuzhiyun use the bit for something different altogether */
220*4882a593Smuzhiyun psb_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
221*4882a593Smuzhiyun psb_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN
224*4882a593Smuzhiyun | ASLE_PFMB_EN;
225*4882a593Smuzhiyun asle->ardy = 1;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun #define ACPI_EV_DISPLAY_SWITCH (1<<0)
230*4882a593Smuzhiyun #define ACPI_EV_LID (1<<1)
231*4882a593Smuzhiyun #define ACPI_EV_DOCK (1<<2)
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun
psb_intel_opregion_video_event(struct notifier_block * nb,unsigned long val,void * data)234*4882a593Smuzhiyun static int psb_intel_opregion_video_event(struct notifier_block *nb,
235*4882a593Smuzhiyun unsigned long val, void *data)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun /* The only video events relevant to opregion are 0x80. These indicate
238*4882a593Smuzhiyun either a docking event, lid switch or display switch request. In
239*4882a593Smuzhiyun Linux, these are handled by the dock, button and video drivers.
240*4882a593Smuzhiyun We might want to fix the video driver to be opregion-aware in
241*4882a593Smuzhiyun future, but right now we just indicate to the firmware that the
242*4882a593Smuzhiyun request has been handled */
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun struct opregion_acpi *acpi;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (!system_opregion)
247*4882a593Smuzhiyun return NOTIFY_DONE;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun acpi = system_opregion->acpi;
250*4882a593Smuzhiyun acpi->csts = 0;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun return NOTIFY_OK;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun static struct notifier_block psb_intel_opregion_notifier = {
256*4882a593Smuzhiyun .notifier_call = psb_intel_opregion_video_event,
257*4882a593Smuzhiyun };
258*4882a593Smuzhiyun
psb_intel_opregion_init(struct drm_device * dev)259*4882a593Smuzhiyun void psb_intel_opregion_init(struct drm_device *dev)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
262*4882a593Smuzhiyun struct psb_intel_opregion *opregion = &dev_priv->opregion;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun if (!opregion->header)
265*4882a593Smuzhiyun return;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun if (opregion->acpi) {
268*4882a593Smuzhiyun /* Notify BIOS we are ready to handle ACPI video ext notifs.
269*4882a593Smuzhiyun * Right now, all the events are handled by the ACPI video
270*4882a593Smuzhiyun * module. We don't actually need to do anything with them. */
271*4882a593Smuzhiyun opregion->acpi->csts = 0;
272*4882a593Smuzhiyun opregion->acpi->drdy = 1;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun system_opregion = opregion;
275*4882a593Smuzhiyun register_acpi_notifier(&psb_intel_opregion_notifier);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
psb_intel_opregion_fini(struct drm_device * dev)279*4882a593Smuzhiyun void psb_intel_opregion_fini(struct drm_device *dev)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
282*4882a593Smuzhiyun struct psb_intel_opregion *opregion = &dev_priv->opregion;
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun if (!opregion->header)
285*4882a593Smuzhiyun return;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun if (opregion->acpi) {
288*4882a593Smuzhiyun opregion->acpi->drdy = 0;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun system_opregion = NULL;
291*4882a593Smuzhiyun unregister_acpi_notifier(&psb_intel_opregion_notifier);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun cancel_work_sync(&opregion->asle_work);
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun /* just clear all opregion memory pointers now */
297*4882a593Smuzhiyun iounmap(opregion->header);
298*4882a593Smuzhiyun opregion->header = NULL;
299*4882a593Smuzhiyun opregion->acpi = NULL;
300*4882a593Smuzhiyun opregion->swsci = NULL;
301*4882a593Smuzhiyun opregion->asle = NULL;
302*4882a593Smuzhiyun opregion->vbt = NULL;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
psb_intel_opregion_setup(struct drm_device * dev)305*4882a593Smuzhiyun int psb_intel_opregion_setup(struct drm_device *dev)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun struct drm_psb_private *dev_priv = dev->dev_private;
308*4882a593Smuzhiyun struct psb_intel_opregion *opregion = &dev_priv->opregion;
309*4882a593Smuzhiyun u32 opregion_phy, mboxes;
310*4882a593Smuzhiyun void __iomem *base;
311*4882a593Smuzhiyun int err = 0;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun pci_read_config_dword(dev->pdev, PCI_ASLS, &opregion_phy);
314*4882a593Smuzhiyun if (opregion_phy == 0) {
315*4882a593Smuzhiyun DRM_DEBUG_DRIVER("ACPI Opregion not supported\n");
316*4882a593Smuzhiyun return -ENOTSUPP;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy);
322*4882a593Smuzhiyun base = acpi_os_ioremap(opregion_phy, 8*1024);
323*4882a593Smuzhiyun if (!base)
324*4882a593Smuzhiyun return -ENOMEM;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun if (memcmp(base, OPREGION_SIGNATURE, 16)) {
327*4882a593Smuzhiyun DRM_DEBUG_DRIVER("opregion signature mismatch\n");
328*4882a593Smuzhiyun err = -EINVAL;
329*4882a593Smuzhiyun goto err_out;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun opregion->header = base;
333*4882a593Smuzhiyun opregion->vbt = base + OPREGION_VBT_OFFSET;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun opregion->lid_state = base + ACPI_CLID;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun mboxes = opregion->header->mboxes;
338*4882a593Smuzhiyun if (mboxes & MBOX_ACPI) {
339*4882a593Smuzhiyun DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
340*4882a593Smuzhiyun opregion->acpi = base + OPREGION_ACPI_OFFSET;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun if (mboxes & MBOX_ASLE) {
344*4882a593Smuzhiyun DRM_DEBUG_DRIVER("ASLE supported\n");
345*4882a593Smuzhiyun opregion->asle = base + OPREGION_ASLE_OFFSET;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun return 0;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun err_out:
351*4882a593Smuzhiyun iounmap(base);
352*4882a593Smuzhiyun return err;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355